关于测试驱动开发的一些困惑

Some confusion about test driven development

根据 wikipedia 以下是 TDD 中的步骤:

第 1 步:编写单元测试

第 2 步:运行 单元测试

第 3 步:为模块编写代码

第 4 步:运行ALL 再次测试

第 5 步:清理代码

第 6 步:重复步骤

问题一:在TDD中我们主要是写单元测试。集成测试和系统测试在上面的步骤中放在哪里?

考虑以下示例:

假设我们有一个必须开发的功能。然后根据 TDD,我们将在短迭代中开发此功能。假设我们将此功能分为 2 个模块——模块 1 和模块 2。我正在编写迭代步骤 根据上述给定的步骤在 TDD 中开发这些模块。 (问题2)以下步骤不对的地方请指正:

**迭代 1:**

第 1 步:我们为模块 1 编写单元测试。

第 2 步:运行模块 1 的此单元测试(此测试会失败)

第 3 步:为模块 1 开发代码。

第 4 步:运行 再次对模块 1 进行单元测试(此测试将通过)

**迭代 2:**

第 1 步:为模块 2 编写单元测试

第 2 步:运行模块 2 的单元测试

第 3 步:为模块 2 编写代码

第 4 步:运行 模块 1 的单元测试和模块 2 的单元测试。(问题 3:在这一步中,我们为什么要 运行 进行单元测试模块 1 作为模块 2 在测试中?如果你说这样做是因为要测试模块 2 是否不会破坏模块 1 的功能那么我的问题是我们在这里只测试模块 2。它还没有与模块 1 集成那么它将如何破坏模块 1?)

**迭代 3:**

第 1 步:创建集成测试(我在这里吗?

第 2 步:运行 集成测试(它们将失败,因为模块 1 和模块 2 尚未集成)

第 3 步:集成模块 1 和模块 2

第 4 步:运行 所有测试(模块 1、模块 2 的单元测试和集成测试)

(问题 4:为什么我们要在这一步 运行 对模块 1 和模块 2 进行单元测试?)

迭代4(所有模块集成时):

第 1 步:创建系统测试

第 2 步:运行 系统测试(它们会失败)

Step 3: (问题5:)这里应该写什么代码作为系统测试我们不写任何代码,根据TDD原则先写测试再写开发代码所以什么我们将在这里编写代码?

测试驱动开发倾向于关注单元,因此也关注单元测试。一种附属的方法,行为驱动开发,或 BDD,在更高层次上驱动设计,从而在更高层次上进行测试;验收测试和集成测试可能在 BDD 中发挥作用。 TDD 不排除集成测试,但它们不是实践的一部分。正如您所说,这种做法是一次为一小部分功能编写一个小测试,使该测试通过,清理您的代码,然后进行迭代。这就是所有的单元测试。

如果您的第一个功能正常工作,那么将您的第二个工作功能与其集成应该可以正常工作 - 至少在理论上是这样。 TDD 为您提供了这两个有效功能,但您当然需要集成它们,当然您还需要测试来验证集成是否有效。但这不是 TDD 实践的一部分。 TDD 所说的是,当您的集成测试失败时,这是某个较小的功能单元失败的结果,而您未能充分测试该功能单元。与其尝试修复它,不如编写演示失败的单元测试。使该测试通过,您的集成测试现在也将通过。系统测试也是如此。

在我看来,您的工作流程略有倒退。在这本优秀的书中 Growing Object Oriented Software Guided By Tests 建议您从更高级别的测试开始,通常是一个 'integration test' 代表您要添加的功能并让它成为您想要看到的行为的驱动程序.

当此集成测试通过时,您就知道您已经完成了该功能。

一旦你有这个失败的测试(作者称之为外循环)然后你开始内循环的 TDD 过程,即为你需要实现的 classes 创建测试所需的功能。您可以通过创建测试、编写代码使其通过来做到这一点。然后 运行 所有测试。您的外部测试可能仍然会失败。然后实施另一个单元测试,然后实施所需的实施。重复此过程,直到您为外部测试创建了所有必要的 classes。

然后通过编写新的外部测试再次重复整个过程。

对我来说,最重要的事情是找到一个适合你的过程,并且要务实。不要被迫采用任何教条的方法,因为与任何事情一样,编写软件会随着您编写的内容、与您一起编写它的人、您可用的工具以及许多其他因素而变化。始终准备好改变您自己的流程并根据您自己的经验以及与您共事和您尊重的人的经验不断重新评估它。没有人有完美的解决方案,因为事情总是会变得更好。

The guy who wrote Rhino Mocks has said he rarely uses mocking frameworks anymore. The guy who wrote Dependency Injection in .NET says he rarely uses IoC containers anymore。灵活务实

我发现我更倾向于关注外部集成测试而不是单元级测试,因为这迫使测试测试代码的行为而不是实现。我发现当我对每个实际 class 进行一次测试 class 时,重构我的代码变得非常昂贵,因为我不仅必须更改所有 classes,而且我有同时重构所有测试。当测试集中在逻辑代码单元的行为时,通常当我重构该代码的 class 结构时,测试保持不变,因为我只是重新组织内部结构,而不是外部行为。

@CarlManaster 所说的关于 BDD 的内容也很相关。我发现 BDD 比 TDD 更有用,主要是因为重点从测试转向了行为。行为是您想要的,而不是一大套测试(尽管这也很好。)

至于要运行进行哪些测试,对我来说测试越多运行越频繁越好。令人惊奇的是,'isolated' 的更改经常会莫名其妙地导致其他东西崩溃。即使对于小型系统,将整个系统中每个更改的后果都记在脑海中也不是您想象的那么简单。

我的首选工具是 NCrunch。它删除了编码的 stop/build/run 测试部分。您只需编写测试,编写代码。等几秒钟。变绿。重复。改变了我的生活;价值是他收费的两倍。