我遇到的问题是单元测试正在修改类Foo
的静态状态而不是自己清理。这意味着单元测试可以在它自己运行时通过,但在其之前运行其他单元测试时会失败,因为状态已经改变。
我一直在查看代码,寻找那些改变Foo
静态状态并添加必要清理的测试,但后来又有了一个想法:为什么不使用junit测试监听器来在每个测试运行后检查Foo
的静态状态,如果Foo
处于修改状态,则会失败?然后我会自动获得每个构建的顽皮测试列表,并且可以在将来识别任何类似的问题。
所以我实现了一个junit RunListener
并修改了我的pom.xml
以将监听器添加到maven-surefire-plugin
,并验证了每个测试用例都执行了监听器。
简单的解决方案是覆盖testFinished()
的{{1}}方法并检查静态:
RunListener
这将适用于以下形式的测试:
public class FooChecker extends RunListener {
@Override
public void testFinished(Description description) throws Exception {
if (fooClassInModifiedState()) {
throw new IllegalStateException("Test " + description.getDisplayName()
+ " has left Foo in a modified state");
}
}
}
}
在每个测试用例之后,调用public class SomeTest {
@Before
public void setUpTest() {
Foo.modifyState();
}
@After
public void tearDownTest() {
Foo.resetState();
}
@Test
public void testCase1() {
...
}
@Test
public void testCase2() {
...
}
方法并清除状态。然后调用junit run listener的@After
方法,并验证状态是否已重置。如果我删除testFinished()
方法,则运行侦听器将按预期引发错误。
这里的问题是junit run listener的@After
方法是在testFinished
之后和单元测试的任何@BeforeClass
方法之前调用的。如果单元测试修改@AfterClass
中的Foo状态并在@BeforeClass
方法中清除,则侦听器的@AfterClass
方法将看到修改后的状态并错误地引发错误:
testFinished
此处,在调用public class SomeTest {
@BeforeClass
public void setUpClass() {
Foo.modifyState();
}
@Test
public void testCase1() {
...
}
@Test
public void testCase2() {
...
}
@AfterClass
public void tearDownClass() {
Foo.resetState();
}
}
和testFinished()
之后调用运行侦听器的testCase1()
方法,但在调用testCase2()
方法之前,所以@AfterClass
仍然被修改,运行侦听器将引发错误。这不是正确的行为。
通常,对于两个单元测试Foo
和TestA
以及一个junit run listener TestB
,调用序列似乎是:
我想要的是在为单元测试调用Listener
方法之后,以及在下一个单元测试中调用@AfterClass
方法之前执行已修改状态的检查。这样我就可以确保单元测试(如果不是单个测试用例)执行必要的清理并仍然尊重测试语义。
但我无法使用junit @BeforeClass
和使用maven surefire插件调用测试来实现此目的。
这里有解决方案吗?
编辑:将junit类规则添加到检查状态的现有测试不是一个选项,因为我需要将它添加到现在存在的每个测试以及将来添加的每个测试。如果可能的话,我想要一些可以自动发现任何违规行为的内容。
编辑:这里的根本问题是maven模块的每个单元测试都在同一个JVM中运行。我尝试在自己的JVM中运行每个单元测试(通过设置surefire的分叉选项),但测试运行时间太长。所以我坚持使用单个JVM进行多单元测试。
编辑:当然,使用静态方法首先是糟糕的设计,并且重构注入状态将是首选,但在此阶段不能重构1,000,000+行的遗留代码库。