JUnit 中是以测试方法为一个独立的生命周期

在研究 JUnit 5 新特性的时候,学习到其中有一节 Test Instance Lifecycle, 才意识到对 JUnit 的理解一直存在一个误区,以为 JUnit 是以测试类为一个生命周期的,其实不然。不管是 JUnit 5 还是 JUnit 4 或更早的版本,JUnit 都是以测试方法为一个独立的生命周期。

只是到了 JUnit 5 提供了方法来把生命周期由方法改为测试类,对于单个测试类可以使用注解 @TestInstance(Lifecycle.PER_CLASS) 来指定用一个测试实例来跑所有的测试方法,这就意味着测试类中的成员变量只被初始化一次。@TestInstance 的 Lifecycle 默认是 PER_METHOD, JUnit 4 就是 PER_METHOD, 而且是不能改的。如果在 JUnit 5 中改变为 PER_CLASS, 恐怕反而会出许多乱子,每个测试方法本就该是完全独立的。

比如在同一个类中多个测试方法使用了同一个实例变量的情况下,总会用一个  @After 方法来复位该实例变量,现在才知道那是多余的。像下面的代码

两个方法引用了同一个实例变量,但是那两个测试用例不管怎么执行都是输出 100, 用不着 @After 方法来复位实例变量 number

这就是因为 JUnit 是以测试方法为一个独立的生命周期,JUnit 框架执行上面那个测试方法时相当于作了如下操作:

而不是

那么我们还要 @After 来做什么呢?如果测试方法是串行执行时,可用它来复位操作的外部资源,例如删除临时文件。

既然 JUnit 是以测试方法为生命周期,那么一个类的多个测试方法是可以并发执行的,这时候它们操作同一资源时要多留点心了。

为加深理解测试方法为生命周期的概念,我们通过下面 JUnit 4 的一个例子来验证

JUnit 4 默认时同一个类中多个方法不会并发执行,可以用 ParallelComputer 开启方法级别的并发执行,这是后话。

我们只说默认情况下,上面的 test1() 总是先执行,然后才 test2()。如果测试方法是一个生命周期,那么 test2() 输出的 number 仍然是默认值  0,而不是 test1() 设置的 100, 看效果

test1 start 1506308523747
test1 end 1506308524249 number 100, instance cc.unmi.CalculatorTest@5d6f64b1
test2 start 1506308524251
test2 end 1506308524251 number 0, instance cc.unmi.CalculatorTest@78e03bb5

没错,尽管 test1 先执行完,设置了 number 为 100, 但 test2 中得到的都是 number 的默认值  0; 因为每次测试方法都是一个新实例,看 this 的实例地址。

之所以有这样一个误区,一部分原因也是被 @BeforeClass 和 @AfterClass 两个注解混淆了视听。

比如 @BeforeClass 很容易令人误以为是在创建好了某个测试用例实例,在执行它任何一个测试方法前唯一执行一次 @BeforeClass 注解的方法。

正确理解 @BeforeClass 和 @AfterClass 应该是,在一次 JUnitCore 运行周期中

@BeforeClass:注解的方法在创建第一个该测试类实例前执行一次

@AfterClass: 注解的方法在执行后该类最后一个测试方法后执行一次

明白了 JUnit 4 是以测试方法为独立的生命周期运转,那么同一个类中多个测试方法是有能力并发执行的。具体做法可参考:Running junit tests in parallel in a Maven build?

还可用 tempus-fugit, 然后测试类用 @RunWith(ConcurrentTestRunner.class)。

用 ParallelComputer 的办法我尝试了以下几种都未能成功

实际中应该值得去研究如何通过配置 maven-surefire-plugin 来支持测试方法并发执行。

我倒是用 @RunWith(ConcurrentTestRunner.class) 看到了上面测试类执行的输出效果

test1 start 1506312917860
test2 start 1506312917860
test2 end 1506312917860 number 0, instance cc.unmi.CalculatorTest@5accd8d6
test1 end 1506312918363 number 100, instance cc.unmi.CalculatorTest@5310aea3

参考:

  1. JUnit - Lifecycle of a Test Class
  2. Running junit tests in parallel in a Maven build?

 

本文链接 https://yanbin.blog/junit-test-instance-lifecycle/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments