为什么单元测试只测试一件事?

iny*_*iny 58 unit-testing

什么是良好的单元测试?说测试应该测试一件事.这有什么好处?

写一些更大的测试来测试更大的代码块会不会更好?无论如何,调查测试失败很困难,我从小测试中看不到它的帮助.

编辑:单词单位并不重要.让我们说我认为单位更大一些.这不是问题所在.真正的问题是为什么对所有方法进行测试或更多测试,因为很少有涵盖许多方法的测试更简单.

一个例子:列表类.我为什么要单独进行添加和删除测试?首先添加的一个测试然后删除声音更简单.

MrB*_*les 77

仅测试一件事就会隔离一件事并证明它是否有效.这是单元测试的想法.测试不止一件事的测试没有错,但这通常被称为集成测试.它们都有基于背景的优点.

举个例子,如果您的床头灯没有打开,并且您更换灯泡并切换延长线,您不知道哪个更改解决了问题.应该进行单元测试,并将您的顾虑分开以隔离问题.

  • 按照定义,“单元”测试一次测试一个程序单元(即一个单元)。 (2认同)

Jon*_*eet 65

我打算在这里走出困境,并说"唯一测试一件事"的建议实际上没有实际帮助,因为它有时会成为现实.

有时候测试需要一定的设置.有时他们甚至可能需要一定的时间来建立(在现实世界中).通常,您可以一次测试两个动作.

Pro:只有所有设置发生一次.第一次行动后的测试将证明世界是您在第二次行动之前的预期.代码更少,测试运行更快.

Con:如果任一操作失败,您将得到相同的结果:相同的测试将失败.与在两个测试中只有一个操作的情况相比,您将获得有关问题所在位置的信息较少.

实际上,我发现这里的"骗局"并不是什么大问题.堆栈跟踪通常会很快缩小范围,我将确保无论如何都要修复代码.

这里稍微不同的"con"是它打破了"编写新测试,使其通过,重构"循环.我认为这是一个理想的循环,但并不总是反映现实.有时,在当前测试中添加额外操作并检查(或可能只是对现有操作的另一个检查)比创建新操作更简单.

  • 和乔恩一样,你可能会陷入困境,但是你正在谈论你选择作为你的栖息地的那个分支. (5认同)
  • @Terry:理论上这听起来很可爱,但在我看来,这并不是最终在实践中100%的工作.如果在某些情况下,通过在单个测试用例中测试两个操作,最终会得到更简单,更小的代码,那么*实际*好处是不是这样做的? (5认同)
  • 单词意味着什么:单元测试应该测试程序的一个单元.一种方法,一种功能.集成和功能测试(可以自动化!)测试更大的块.我也被低估了,因为提问者似乎已经有了答案,而忽略了更多的赞成回答. (2认同)

swi*_*ams 12

通常不推荐检查多件物品的测试,因为它们更紧密耦合且易碎.如果您在代码中更改某些内容,则更改测试需要更长时间,因为需要考虑更多事项.

[编辑:]好的,说这是一个示例测试方法:

[TestMethod]
public void TestSomething() {
  // Test condition A
  // Test condition B
  // Test condition C
  // Test condition D
}
Run Code Online (Sandbox Code Playgroud)

如果您对条件A的测试失败,那么B,C和D似乎也会失败,并且不会为您提供任何有用的信息.如果你的代码更改会导致C失败怎么办?如果您将它们拆分为4个单独的测试,您就会知道这一点.


New*_*ian 11

哈...单元测试.

将任何"指令"推得太远,它会迅速变得无法使用.

单个单元测试测试单个事物就像单个方法执行单个任务一样好.但IMHO并不意味着单个测试只能包含一个断言语句.

@Test
public void checkNullInputFirstArgument(){...}
@Test
public void checkNullInputSecondArgument(){...}
@Test
public void checkOverInputFirstArgument(){...}
...
Run Code Online (Sandbox Code Playgroud)

比...更好

@Test
public void testLimitConditions(){...}
Run Code Online (Sandbox Code Playgroud)

在我看来是品味的问题,而不是良好的实践.我个人更喜欢后者.

@Test
public void doesWork(){...}
Run Code Online (Sandbox Code Playgroud)

实际上是"指令"要求你不惜一切代价避免的,以及最快的耗尽我的理智.

作为最后的结论,将语义相关且易于测试的事物组合在一起,以便失败的测试消息本身实际上足够有意义,可以直接转到代码.

这里有一个失败的测试报告的经验法则:如果你必须首先阅读测试代码,那么你的测试结构不够好,需要更多分成更小的测试.

我的2美分.


Bea*_*ler 7

想想建一辆车.如果你要应用你的理论,只测试大事,那么为什么不进行测试以驾驶汽车穿越沙漠.它崩溃了.好的,请告诉我导致问题的原因.你不能.这是一个场景测试.

功能测试可能是打开引擎.它失败.但这可能是由于多种原因造成的.你仍然无法确切地告诉我是什么导致了这个问题.我们越来越近了.

单元测试更具体,并且将首先确定代码被破坏的位置,但它也将(如果正确执行TDD)帮助将代码构建为清晰的模块化块.

有人提到过使用堆栈跟踪.算了吧.那是第二个度假胜地.通过堆栈跟踪或使用调试是一件痛苦的事情并且可能非常耗时.特别是在较大的系统和复杂的错误上.

单元测试的良好特性:

  • 快(毫秒)
  • 独立.它不受其他测试的影响或依赖于其他测试
  • 明确.它不应该膨胀,或包含大量的设置.


tva*_*son 6

使用测试驱动开发,您将首先编写测试,然后编写代码以通过测试.如果你的测试是专注的,这使得编写代码更容易通过测试.

例如,我可能有一个带参数的方法.我首先想到的一件事是,如果参数为null,会发生什么?它应该抛出一个ArgumentNull异常(我认为).所以我编写了一个测试,检查在传递null参数时是否抛出该异常.运行测试.好的,它会抛出NotImplementedException.我通过更改代码来修复它以抛出ArgumentNull异常.运行我的测试它通过.然后我想,如果它太小或太大会发生什么?啊,那是两个测试.我先写一个太小的案子.

关键是我没有同时考虑该方法的行为.我通过考虑它应该做什么来逐步(和逻辑地)构建它,然后实现代码和重构,因为我去使它看起来漂亮(优雅).这就是为什么测试应该小而且集中,因为当你考虑行为时,你应该以小的,可理解的增量进行开发.