1

我遇到了一个问题,单元测试正在修改类的静态状态Foo而不是自行清理。这意味着一个单元测试在它自己运行时可能会通过,但是当其他单元测试在它之前运行时会失败,因为状态已经改变。

我一直在通过代码寻找那些改变静态状态Foo并添加必要清理的测试,但后来我有了另一个想法:为什么不使用 junit 测试侦听器来检查Foo每个测试运行后的静态状态,如果Foo处于修改状态则失败?然后我会自动获得每个构建的顽皮测试列表,并且可以在将来识别任何类似的问题。

所以我实现了一个 junitRunListener并修改了 mypom.xml以将侦听器添加到maven-surefire-plugin,并验证了每个测试用例都执行了侦听器。

简单的解决方案是覆盖testFinished()方法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() {
    ...
  }

}

在每个测试用例之后,@After调用该方法并清理状态。然后testFinished()调用junit run listener的方法,验证状态是否已经重置。如果我删除该@After方法,则运行侦听器将按预期引发错误。

这里的问题是testFinishedjunit run listener的方法是在单元测试的@BeforeClass任何方法之后和之前调用的。@AfterClass如果单元测试在 a 中修改了 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()run listener的方法是在被调用之后testCase1()testCase2()已经被调用,但是在@AfterClass方法被调用之前,所以状态Foo仍然是修改的,run listener会报错。这不是正确的行为。

一般来说,对于两个单元测试TestATestB一个 junit 运行监听Listener器,调用顺序似乎是:

  • Listener.testRunStarted()
  • TestA.@BeforeClass
  • Listener.testStarted("TestA.testCase1)
  • 测试A.@之前
  • 测试A.testCase1()
  • 测试A.@之后
  • Listener.testFinished(TestA.testCase1)
  • Listener.testStarted("TestA.testCase2)
  • 测试A.@之前
  • 测试A.testCase2()
  • 测试A.@之后
  • Listener.testFinished(TestA.testCase2)
  • 测试A.@AfterClass
  • TestB.@BeforeClass
  • Listener.testStarted("TestB.testCase1)
  • 测试B.@之前
  • TestB.testCase1()
  • 测试B.@之后
  • Listener.testFinished(TestB.testCase1)
  • TestB.@AfterClass
  • Listener.testRunFinished()

我想要的是在为单元测试调用该方法之后以及在下一个单元测试调用@AfterClass该方法之前执行修改状态的检查。@BeforeClass这样我可以确保单元测试(如果不是单独的测试用例)执行必要的清理并且仍然尊重测试语义。

但是我看不到使用 junitRunListener和使用 maven surefire 插件调用测试的方法。

这里有解决方案吗?

编辑:将junit类规则添加到检查状态的现有测试不是一个选项,因为我需要将它添加到现在存在的每个测试以及将来添加的每个测试中。如果可能的话,我想要一些能够自动发现任何违规行为的东西。

编辑:这里的根本问题是 Maven 模块的每个单元测试都在同一个 JVM 中运行。我尝试在自己的 JVM 中运行每个单元测试(通过设置 surefire 的分叉选项),但测试运行时间太长。所以我坚持使用单个 JVM 进行多个单元测试。

编辑:当然,首先使用静态方法是不好的设计,重构注入状态是首选,但在这个阶段重构超过 1,000,000 行的遗留代码库不是一个选项。

4

0 回答 0