测试列表…针对每种情况都在同一测试中还是在一项测试中?


21

我正在测试某个功能是否可以实现列表中的预期功能。所以我要测试

f(null) -> null
f(empty) -> empty
f(list with one element) -> list with one element
f(list with 2+ elements) -> list with the same number of elements, doing what expected

为此,最好的方法是什么?

  • 在同一(方法)测试中测试所有案例,名称为“ WorksAsExpected”
  • 针对每种情况进行一次测试,因此
    • “ WorksAsExpectedWhenNull”
    • “ WorksAsExpectedWhenEmpty”
    • “ WorksAsExpectedWhenSingleElement”
    • “ WorksAsExpectedWhenMoreElements”
  • 我没有想到的另一种选择:-)


2
我将这些作为单独的测试用例编写。如果您的测试系统支持,则可以使用参数化测试。
jonrsharpe

5
如果您以给定的方式编写测试...时间...然后风格,那么不言而喻,应该分别进行测试...
Robbie Dee

1
我想补充一下:IMO,将边缘情况(如null和empty)分离到单独的测试中是很好的,因为这些情况经常涉及跨不同可能实现的特殊情况逻辑,如果这些测试失败,它们将在被测代码失败的方式是什么(您不必更深入地研究或调试测试用例,以了解发生了什么事情)。
FilipMilovanović18年

1
列出重复元素?
atayenel

Answers:


30

对于在一个或多个测试用例中执行一组测试的简单经验法则是:它仅涉及一个设置吗?

因此,如果我测试了多个元素,它们都处理了所有元素并得出了正确的结果,则我可能有两个或更多个断言,但是我只需要设置一次列表即可。因此,一个测试用例就可以了。

但是在您的情况下,我必须设置一个空列表,一个空列表等。这是多种设置。因此,在这种情况下,我肯定会创建多个测试。

正如其他人所提到的那样,那些“多重测试”可能能够作为单个参数化测试用例存在。即,针对各种设置数据运行相同的测试用例。要知道这是否是可行的解决方案,关键在于测试的其他部分:“动作”和“断言”。如果您可以对每个数据集执行相同的操作并进行断言,请使用此方法。if例如,如果您发现添加来针对数据的不同部分运行不同的代码,那么这不是解决方案。在后一种情况下,请使用单个测试用例。


14

需要权衡。您在一项测试中包装的次数越多,尝试通过的洋葱效应就越有可能。换句话说,第一个失败会停止该测试。在修复第一个故障之前,您将不知道其他断言。就是说,拥有一堆除设置代码外大部分都非常相似的单元测试,只是为了发现有些工作是书面的而其他工作却不是,这些工作非常繁琐。

可能的工具,基于您的框架:

  • 理论。理论可以让您测试有关一组数据的一系列事实。然后,框架将为您的测试提供多种测试数据方案-通过字段或生成数据的静态方法。如果您的一些事实是基于某些前提的,而其他事实却没有,这些框架就会引入假设的概念。Assume.that()如果前提条件不合格,您只需跳过数据测试。这使您可以定义“按预期工作”,然后简单地输入大量数据。查看结果时,您有一个用于父测试的条目,然后是每个数据的子条目。
  • 参数化测试。参数化测试是理论的先驱,因此可能没有理论可以进行的前提条件检查。最终结果是相同的。您查看结果,对于测试本身有一个父项,然后为每个数据点有一个特定项。
  • 一种具有多个断言的测试。进行设置所需的时间更少,但最终一次发现一点问题。如果测试时间太长,并且测试了太多不同的场景,则存在两个大风险:它将需要很长时间才能运行,并且您的团队将对此感到厌倦并关闭测试。
  • 具有类似实现的多个测试。重要的是要注意,如果断言不同,则它们的测试不会重叠。但是,这将是TDD团队的传统智慧。

我并不严格认为assert您的测试中只能有一个语句,但是我确实提出了所有断言都应该测试单个操作的后置条件的限制。如果测试之间的唯一区别是数据,那么我的想法是使用更高级的数据驱动测试功能,例如参数化测试或理论。

权衡您的选择以决定最佳结果。我会说“ WorksAsExpectedWhenNull”与您处理具有不同数量元素的集合的任何情况都有根本不同。


5

这些是不同的测试用例,但是测试的代码是相同的。因此,使用参数化测试是最佳解决方案。如果您的测试框架不支持参数化,则将共享代码提取到帮助器函数中,然后从各个测试用例中调用它。

尝试通过一个测试用例中的一个循环来避免参数化,因为这样很难确定哪个数据集导致了错误。

在TDD红绿重构周期中,您应该一次添加一个示例数据集。将多个测试用例组合到参数化测试中将是重构步骤的一部分。

一种完全不同的方法是属性测试。您将创建各种(参数化的)测试来声明函数的各种属性,而无需指定具体的输入数据。例如,属性可以是:对于所有列表xs,列表ys = f(xs)的长度与相同xs。然后,测试框架将生成有趣的列表和随机列表,并断言您的属性对所有这些属性均有效。这与手动指定示例有所不同,因为手动选择示例可能会错过有趣的边缘情况。


最后一句中的“未命中”是否应该“找到”?
罗比迪

@RobbieDee英语是不明确的,固定的。
阿蒙(Amon)

3

对每种情况进行一次测试是适当的,因为在每次测试中测试单个概念是一个很好的指南,通常会被推荐。

看到这篇文章:在一个单元测试中可以有多个断言可以吗?。那里也有相关且详细的讨论:

我的指导原则通常是每次测试都测试一个逻辑概念。您可以在同一对象上具有多个断言。它们通常是要测试的相同概念。 资料来源-罗伊·奥什罗夫(Roy Osherove)

[...]

测试仅应出于一个原因而失败,但这并不总是意味着仅应存在一个Assert语句。恕我直言,保持“安排,行动,断言”模式更为重要。

关键是您只有一个操作,然后使用断言检查该操作的结果。但这是“安排,行动,断言,测试结束”。如果您想通过执行其他操作来继续测试,然后再进行更多声明,请改为进行单独的测试。资源


0

我认为,这取决于测试条件。

  • 如果您的测试只有一种条件可以设置测试,但副作用很多。多断言是可以接受的。
  • 但是,当您有多个条件时,即意味着您有多个测试用例,则每个测试用例应仅包含1个单元测试。

这读起来更像是一条评论,请参阅“ 如何回答
gna
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.