使用When和Should以及TDD学习单元测试

4
我新工作的测试与我以前遇到过的测试完全不同。在编写单元测试时(很可能是在编写代码之前),他们创建了一个以“When”开头的类。其名称描述了测试运行情况下的场景(测试对象)。他们将为代码中的每个分支创建子类。该类中的所有测试都以“should”开头,并在运行后测试代码的不同方面。因此,他们将为验证每个模拟(DOC)正确调用和检查返回值(如果适用)的每个测试方法运行相同的执行代码。但这似乎很浪费,会导致混乱。我想知道他们是否使用了类似的技术。对于这种风格及其应该如何实施的链接将是很好的资源。它听起来类似于我见过的某些BDD方法。
我还注意到,他们已经将“执行”SUT的重复调用移到了设置方法中。这会导致问题,因为当他们期望发生异常时,无法使用内置工具进行检查(Python unittest的assertRaises)。这还意味着将返回值存储为测试类的支持字段。他们还必须将许多模拟存储为支持字段。跨类层次结构,这变得很难确定每个模拟的配置。
他们也以稍微不同的方式测试代码。这实际上取决于他们认为什么是集成测试。他们模拟一切会从正在测试的函数中夺走上下文的内容。这可能意味着同一类中的私有方法。我一直将模拟限制在可能影响测试结果的资源上,例如数据库、文件系统或日期。我可以看到这种方法的一些价值。但是,现在正在使用的方式,我可以看到它会导致脆弱的测试(每次代码更改都会破坏测试)。我担心因为在这种情况下没有集成测试,你可能会错误地使用第三方API,但您的单元测试仍然可以通过。我想了解更多关于这种方法的信息。
因此,如果有任何关于更多了解这些方法的资源,那肯定是好的。我不想因为不理解他们的做法而放弃学习机会。我也希望停止关注这些方法的负面影响,并看看其好处所在。

看看Roy Osherove的资源 - 博客 - 单元测试、BDD的艺术 - GSerjo
2个回答

3
如果我正确理解了您在第一段中的解释,那么这与我经常做的事情非常相似。(取决于测试框架是否易于使用。此外,许多mocking框架不支持它,但是像Mockito这样的spy框架做得更好。)例如,请参见此处的堆栈示例,其中有一个常见的设置(向堆栈添加内容),然后有一堆独立的测试,每个测试都检查一件事情。这里还有另一个例子,这次没有一个测试(@Test)修改了公共fixture(@Before),但是它们中的每一个都专注于检查应该发生的一个独立的事情。如果测试非常专注,那么就应该可以更改生产代码,使任何单个测试失败,而所有其他测试都通过(我最近在单元测试焦点隔离中写过这方面的内容)。
主要思想是每个测试检查一个特定的功能/行为,这样当测试失败时更容易找出失败原因。请参阅TDD教程以获取更多示例并学习该风格。
我不担心执行相同代码路径多次,如果运行一个测试需要一毫秒(如果运行所有单元测试需要超过几秒钟, 那么测试可能太大了)。从您的解释中,我更担心测试可能与实现过于紧密耦合,而不是与特性相关,如果每个模拟都有一个测试,那么测试名称将是衡量测试结构良好或脆弱程度的良好指标 - 它描述了一个特征还是如何实现该特征。
关于mocking,一本不错的书是《Growing Object-Oriented Software Guided by Tests》。对于第三方API(你不拥有和无法修改的API),正如你已经提到的原因,应该避免mock它们,但是应该创建一个更适合系统需要并按照你想要的方式工作的抽象层。这个抽象层需要与第三方API进行集成测试,但在所有使用该抽象层的测试中,可以对其进行mock。

我希望Python有一个类似的框架存在。我发现我的测试技能有点落后了。我会继续阅读这些例子。太棒了! - Travis Parks
我完全理解为什么这种方法比在单元测试底部验证少量模拟更好。 - Travis Parks

1
首先,您正在使用的模式基于Cucumber - 这是一个链接。该风格来自BDD(行为驱动开发)方法。它比传统的TDD有两个优点:
  1. 语言 - BDD的原则之一是您使用的语言会影响您的思维方式,因为它强制您使用最终用户的语言进行交流,这将导致您编写不同的测试,而不是从程序员的角度编写测试。
  2. 测试锁定代码 - BDD在适当的级别上锁定代码。测试中常见的一个问题是编写大量测试,这使得您的代码库更加脆弱,因为当您更改代码时,您还必须更改大量测试。BDD强制您锁定代码的行为,而不是代码的实现方式。这样,当测试失败时,它更有可能具有意义。
值得注意的是,您不必使用Cucumber风格的测试来实现这些效果,使用它确实会增加额外的开销。但很少有程序员能够成功地在使用传统的xUnit工具(TDD)时保持BDD的心态。

听起来你有一些场景需要说“当我做了某事,然后验证”。因为当前的BDD xUnit框架只允许您验证基本类型(字符串、整数、双精度浮点数、布尔值等),这通常会导致大量单独的测试(每个Assert一个)。使用Golden Master范式测试工具(例如ApprovalTests)可以进行更复杂的验证。这里有一个视频示例

最后,这是Dan North的博客链接-他开创了这一切。


今天我开始使用一个名为Pea的库,它是Python的类似Cucumber的工具,精简到了基本要素。但是我遇到了一些问题。主要是在多个测试中看到相同的代码重复出现。此外,尽管这些测试读起来像散文,但很少强调测试之间的差异。有没有什么技巧可以专注于测试之间的差异并消除重复? - Travis Parks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接