【问题标题】:Learning About Unit Testing Using When and Should and TDD学习使用When和Should和TDD进行单元测试
【发布时间】:2012-08-06 21:59:37
【问题描述】:

我的新工作中的测试与我之前遇到的测试完全不同。

当他们编写单元测试时(大概在代码之前),他们会创建一个以“When”开头的类。该名称描述了测试将运行的场景(夹具)。他们将通过代码为每个分支创建子类。类中的所有测试都以“应该”开头,并在运行后测试代码的不同方面。因此,他们将有一种方法来验证每个模拟(DOC)是否被正确调用并检查返回值(如果适用)。我对这种方法有点困惑,因为这意味着每个测试都运行完全相同的执行代码,这似乎很浪费。我想知道是否有类似的技术他们可能已经适应了。一个解释样式及其应该如何实现的链接会很棒。我听起来类似于我见过的一些 BDD 方法。

我还注意到他们已经将重复调用“执行”SUT 移到了设置方法中。当他们期待异常时,这会导致问题,因为他们不能使用内置工具来执行检查(Python unittest 的 assertRaises)。这也意味着将返回值存储为测试类的支持字段。他们还必须存储许多模拟作为支持字段。跨类层次结构很难分辨每个模拟的配置。

他们对代码的测试也略有不同。这真的归结为他们认为的集成测试。他们模拟出任何从被测试函数中窃取上下文的东西。这可能意味着同一类中的私有方法。我一直将模拟限制在可能影响测试结果的资源上,例如数据库、文件系统或日期。我可以看到这种方法的一些价值。但是,现在使用它的方式,我可以看到它导致脆弱的测试(每次代码更改都会中断的测试)。我很担心,因为如果没有集成测试,在这种情况下,您可能会错误地使用第 3 方 API,但您的单元测试仍然会通过。我也想详细了解这种方法。

因此,任何有关在哪里了解有关其中一些方法的更多信息的资源都会很好。我不想因为我不了解他们做事的方式而放弃一个很好的学习机会。我也想停止关注这些方法的负面影响,看看有什么好处。

【问题讨论】:

标签: unit-testing tdd bdd


【解决方案1】:

如果我正确理解了您在第一段中的解释,那与我经常做的非常相似。 (取决于测试框架是否使它变得容易。还有许多模拟框架不支持它,但像Mockito 这样的间谍框架做得更好。)

例如查看stack example here,它有一个共同的设置(将东西添加到堆栈中),然后是一堆独立的测试,每个测试都检查一个东西。 Here's still another example,这一次没有任何测试 (@Test) 修改公共夹具 (@Before),但每个测试都专注于检查应该发生的一件独立事情。如果测试非常集中,那么应该可以更改生产代码以使任何单个测试失败而所有其他测试都通过(我最近在Unit Test Focus Isolation 中写过)。

主要想法是让每个测试检查单个功能/行为,以便在测试失败时更容易find out why it failed。有关更多示例并了解这种风格,请参阅 this TDD tutorial

我不担心多次执行相同的代码路径,当运行一个测试需要一毫秒时(如果需要more than a couple of seconds to run all unit tests,测试可能太大了)。从您的解释来看,如果每个模拟都有一个测试是系统的,我更担心测试可能与实现过于紧密地耦合,而不是功能。测试的名称可以很好地表明测试的结构有多好或有多脆弱 - 它是否描述了一个特性或该特性是如何实现的。

关于嘲笑,一本好书是Growing Object-Oriented Software Guided by Tests。由于您已经提到的原因,不应模拟第 3 方 API(您不拥有且无法修改的 API),但应在其上创建一个抽象,以更好地满足使用它的系统的需求并工作你想要的方式。该抽象需要与第 3 方 API 进行集成测试,但在所有测试中使用您可以模拟它。

【讨论】:

  • 我希望 Python 有一个类似的框架。我可以看到我的测试技能有点落后。我将继续阅读这些示例。好东西!
  • 我完全明白为什么这种方法比在单元测试的底部验证少数模拟更好。
【解决方案2】:

首先,您使用的模式基于 Cucumber - 这里是 link。风格来自 BDD(行为驱动开发)方法。与传统 TDD 相比,它有两个优势:

  1. 语言 - BDD 的租户之一是,您使用的语言会影响您的想法,迫使您以最终用户的语言说话,您最终会编写不同的测试,而不是专注于编写测试程序员
  2. 测试锁定代码 - BDD 将代码锁定在适当的级别。测试中常见的一个问题是您编写了大量测试,这使您的代码库更加脆弱,因为当您更改代码时,您也必须更改大量测试。 BDD 强制您锁定代码的行为,而不是代码的实现。这样,当测试中断时,它更有可能是有意义的。

值得注意的是,您不必使用 Cucumber 测试风格来实现这些影响,并且使用它确实会增加额外的开销层。但很少有程序员在使用传统 xUnit 工具 (TDD) 的同时成功地保持 BDD 思维方式。

听起来您在某些情况下想说“当我这样做时,然后验证”。因为当前的 BDD xUnit 框架只允许您验证原语(字符串、整数、双精度、布尔值......),这通常会导致大量单独的测试(每个 Assert 一个)。可以使用Golden Master 范式测试工具进行更复杂的验证,例如ApprovalTests。这是video example 这个。

最后,这是 Dan North 的blog 的链接 - 他开始了这一切。

【讨论】:

  • 今天我开始使用一个名为 Pea 的库,它是一个类似 Cucumber 的 Python 工具,只包含最基本的功能。不过,我遇到了问题。大多数情况下,我看到相同的代码在多个测试中重复。此外,即使测试读起来像散文,也很少强调测试之间的变化。他们的技术是否专注于测试之间的哪些变化并消除重复?
猜你喜欢
  • 2012-04-21
  • 1970-01-01
  • 2015-04-27
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-25
相关资源
最近更新 更多