【问题标题】:Equivalent to @DirtiesContext(...) for Surefire + JUnit?相当于 Surefire + JUnit 的 @DirtiesContext(...)?
【发布时间】:2021-08-04 20:46:29
【问题描述】:

我正在使用 maven-surefire-pluginjunit 4.1.4。我有一个单元测试,它依赖于内部使用static { ... } 代码块来启动一些变量的第 3 方类。对于一项测试,我需要更改这些变量之一,但仅限于某些测试。我希望在测试之间重新执行这个块,因为它在第一次运行时会获取一个值。

在测试时,似乎 Surefire 将测试类实例化一次,因此 static { ... } 代码块永远不会再次处理。

这意味着我的单元测试更改了测试所需的值被忽略了,静态类已经被实例化了。

???? 注意:静态类使用System.loadLibrary(...),据我发现,它不能被重写以实例化,static 是(罕见但)正确用法。

我为 Spring Framework 找到了一个类似的解决方案,它使用 @DirtiesContext(...) 注释,允许程序员将类或方法标记为“脏”,以便在测试之间初始化一个新类(或在许多情况下是 JVM)。

你如何做与@DirtiesContext(...)相同的事情,但使用maven-surefire-plugin

public class MyTests {
    @Test
    public void test1() {
        assertThat(MyClass.THE_VALUE, is("something-default"));
    }

    @Test
    public void test2() {
        System.setProperty("foo.bar", "something-else");
        assertThat(MyClass.THE_VALUE, is("something-else"));
        //                            ^-- this assert fails
        //                                value still "something-default"
    }
}
public class MyClass {
    static {
        String value;
        if(System.getProperty("foo.bar") != null) {
            value = System.getProperty("foo.bar"); // set to "something-else"
        } else {
            value = "something-default";
        }
    }
    public static String THE_VALUE = value;

}
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>4.1.2</version>
      </plugin>

【问题讨论】:

  • Surefire 与它几乎没有什么关系——它是控制测试类(JUnit、JUnit5 等)生命周期的测试引擎。你可以让surefire分叉出多个JVM——但这很慢。
  • @BoristheSpider 我更新了问题以指定我正在使用 Junit 4。这有助于澄清问题吗?我可以分叉另一个 JVM,这就是实践中的做法。关于surefire-maven-pluginJUnit,我大多只是使用@Test 注释,所以我仍然不确定如何解决静态类问题。我还添加了一个伪代码 sn-p。
  • 另外,reuseForks 设置为 false 似乎已损坏:issues.apache.org/jira/browse/SUREFIRE-1534编辑: 升级到 Surefire 3.0.0-M4 解决了这个问题。
  • 不幸的是碰到 Surefire 3.0.0-M4 并设置 reuseForks false 并不能解决这个问题,因为它是同一个测试类,它仍然失败。还有其他方法吗?

标签: java maven maven-surefire-plugin


【解决方案1】:

Java 中的静态初始化块是 JUnit 无法轻松处理的。一般来说,静态的东西不能很好地与单元测试概念配合使用。

因此,假设您无法触摸此代码,您的选择是:

选项 1:

为每个测试生成一个新的 JVM - 好吧,这会起作用,但可能有点矫枉过正,因为它会加剧性能

如果您遵循这条路径,您可能需要配置surefire插件:

forkCount=1
reuseForks=false

根据surefire plugin documentation,这个组合将在自己的JVM进程中执行每个测试类。

选项 2:

为每个测试使用不同的类加载器创建一个类。

如果类com.foo.A由ClassLoader M创建,基本上在Java中与由ClassLoaded N创建的同一个类com.foo.A完全不同。 这有点hacky,但应该可以。

开销比选项 1 小得多。但是您必须了解如何将新的类加载器“合并”到测试基础架构中。

有关创建自定义类加载器的更多信息,请阅读例如this tutorial

【讨论】:

  • 谢谢。第一个解决方案详细讨论了 OP 的 cmets,但为了完整起见:“如果你不能触摸此代码”可以触摸代码,但该类使用 System.loadLibrary(...),它是有效静态的,因此无法重新实例化任何东西,所以选择不理会它,因为它实际上更准确。目前,将测试添加到单独的类中并使用&lt;reuseFork&gt;false&lt;/reuseFork&gt; 是解决方法,但我不认为这是一个解决方案。关于类加载器,我同意,它很老套,从单元测试的角度来看似乎更令人困惑。
  • “一般来说,静态的东西不能很好地与单元测试概念配合使用。”我不明白这个说法。像java.library.path 这样简单的东西实际上是静态的,可以影响许多(许多)项目,所以这种banish all static 实用主义会伤害使用真实世界推荐技术的项目。如果可能的话,我很乐意在具有差异化属性的 for 循环中重新调用所有单元测试。这种没有什么是静态的概念对某些东西很重要,对其他东西则不然。每次都创建新类不是解决方案,而是插件行为的解决方法。
  • 注意,我也接受过类似System.unloadLibrary(...) 范式的方法来允许正确的取消实例化,但我找不到这样的 API。:) 我已经更新了问题以解释为什么 @987654331 @ 正在(罕见但正确)被使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-29
相关资源
最近更新 更多