每个单元测试是否应该能够独立于其他测试运行?


24

假设您有针对类的两种方法的测试。第一种方法从另一层收集数据,并将其放入独立于运行时的某种存储中(例如SQL表),因此,此测试处理的所有数据都被硬编码到测试中。第二种方法负责从第一种方法保留的位置获取数据并以某种方式进行转换(计算,将某些零件移动到其他位置等)。

现在,第二种方法可以像第一种方法那样对输入进行硬编码,或者可以假设这两个测试将按顺序运行,并且可以从第一个测试中断的位置开始,获取第一个测试真正存储的数据。

如果您选择了第二个选项,那么您确实会很好地认为这两种方法可以很好地协同工作,但是,如果第一个测试失败了,则所有测试都将失败,从而失去了帮助更快地隔离错误的测试优势。

如果选择第一种方法,则每种方法都将被隔离和独立测试,但是您永远不会真正知道它们是否可以真正正常地协同工作。

这里哪个更好?是否存在某种替代方法,例如对每个隔离的方法都使用硬编码进行一次测试,然后将两个方法合而为一的更大的测试?


2
我实际上希望我可以在每次运行时轻松地将单元测试的顺序随机化。现在它们以某种未知的顺序运行,尽管顺序相对固定。
工作

Answers:


11

如果选择第一种方法,则每种方法都将被隔离和独立测试,但是您永远不会真正知道它们是否可以真正正常地协同工作。

如果您的方法真正独立,那就没关系。您的第二种方法应:

a)当显示有效数据时,可以正常工作。

b)出现无效数据时,会明智而持续地失败。

同样,您的第一种方法应该执行相同的操作。因此,只要您处理错误情况,它们就可以正常工作。

如果您想测试这些方法是否可以一起正常工作,那就是集成测试,而不是单元测试。


27

如果测试不能独立运行,则它们不是单元测试。

单元测试不应依赖任何外部状态,例如数据库表的内容。它应该纯粹地隔离测试一个代码单元。

更改或需要某种状态的测试是有效的,例如,它们可能构成集成测试的一部分,在这种情况下,确保完成适当的设置很重要,但这些不是单元测试。在这种情况下,我仍然不建议一项测试需要运行另一项。如果是这种情况,您可能应该将所需的代码分解为单独的设置方法。例如,您可能有一个测试,然后仅调用设置代码并验证没有抛出异常,然后另一个测试主动使用set-up方法中设置的数据。


@Steve,因此在此示例中,您会说:一种方法1的测试,一种方法2的测试,以及在同一测试中运行1和2的一个测试?
Morgan Herlocker 2011年

2
是。前两个是单元测试,第三个听起来像是集成测试。
史蒂夫

如果您有客户模块和订单模块,则无法创建与客户无关的订单。如何独立于客户模块进行测试:使用sql在数据库中创建客户记录(插入客户)或使用Customer.createCustomer()。恕我直言,使用第二种方法更好,因为您不需要在测试中加入任何逻辑,但是只有在您创建客户的测试通过的情况下,它才有效。
Dainius

@Dainius。在单元测试方案中,通常将使用模拟对象,因此会将模拟客户传递给订单模块。您是对的,在这种情况下,您不想使用sql。
史蒂夫

似乎在方法B依赖于方法A的任何情况下,几乎总是有方法C先调用A然后再调用B。既然是这种情况,则可以独立测试A,B和C。
Morgan Herlocker 2011年

9

我确定现在有依赖于单元测试B的状态的单元测试B似乎是可以的。但是请考虑从现在开始的一年,那时您有上千个单元测试。您是否真的要等待十分钟,以使整个测试套件在每次需要更改时都完成?

当然,这取决于您的开发风格,但是如果您希望有一个体面的测试驱动开发的希望,即在开发功能时您可能多次运行单个测试,建议您为每个测试提供独立的能力。


1
+1 Raskolnikov,我没有考虑过这样的事实,那就是稍后在进行“所有测试”时会浪费大量时间。
Morgan Herlocker 2011年

3

听起来您在谈论测试设置,可以通过多种方式执行。您需要每个测试的测试数据的干净副本(称为夹具),因此它们中的每个都不应相互依赖。

有几种框架可以进行这种类型的测试,而DBUnit之类的工具可以让您在测试和测试套件的开始和结束时快速建立和拆除数据结构。

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.