JUnit 5 中的 @TestInstance 注释有什么用?

What use is @TestInstance annotation in JUnit 5?

您能否简单解释一下 @TestInstance 注解及其在 JUnit 5 中的用途?

我想我们可以通过使我们的字段static.

达到同样的效果

我认为the docs提供了一个有用的总结:

If you would prefer that JUnit Jupiter execute all test methods on the same test instance, simply annotate your test class with @TestInstance(Lifecycle.PER_CLASS). When using this mode, a new test instance will be created once per test class. Thus, if your test methods rely on state stored in instance variables, you may need to reset that state in @BeforeEach or @AfterEach methods.

The "per-class" mode has some additional benefits over the default "per-method" mode. Specifically, with the "per-class" mode it becomes possible to declare @BeforeAll and @AfterAll on non-static methods as well as on interface default methods. The "per-class" mode therefore also makes it possible to use @BeforeAll and @AfterAll methods in @Nested test classes.

但您可能已经读过,您认为将字段设为静态与将字段声明为实例变量并使用 @TestInstance(Lifecycle.PER_CLASS).

具有相同的效果,您的想法是正确的

所以,也许“它在 JUnit 5 中如何发挥作用”这个问题的答案是使用 @TestInstance ...

  • 明确表达您的意图。可以假设使用 static 关键字是偶然的,而使用 @TestInstance 不太可能是偶然的,或者是粗略的复制粘贴的结果。
  • 将管理范围和生命周期的责任委托给框架,而不是必须记住自己管理。

引入此注释是为了减少 运行 单元测试时创建的对象数量。

@TestInstance(TestInstance.Lifecycle.PER_CLASS) 添加到您的测试 class 将避免为 class 中的每个测试创建您的 class 的新实例。 当您在同一个测试中有很多测试 class 并且此 class 的实例化很昂贵时,这特别有用。

应谨慎使用此注释。所有单元测试都应该相互隔离和独立。如果其中一项测试更改了测试的状态 class,那么您不应使用此功能。

使您的字段静态化以达到相同的效果并不是一个好主意。它确实会减少创建的对象数量,但是当测试 class 中的所有测试都执行时,它们无法被清理。当你有一个巨大的测试套件时,这可能会导致问题。

@TestInstance用于配置测试实例的生命周期用于注解测试class或测试接口:

  • PER_CLASS: 每个测试都会创建一个新的测试实例class.
  • PER_METHOD:将为每个测试方法、测试工厂方法或测试模板方法创建一个新的测试实例。此模式类似于 JUnit 版本 1 到 4 中的行为。

如果 @TestInstance 未在测试 class 或由测试 class 实现的测试接口上显式声明,则生命周期模式将隐式默认为 PER_METHOD .


将测试实例生命周期模式设置为 PER_CLASS 可启用以下功能:

  • 给定测试 class 中的测试方法之间以及测试 class 中的非静态 @BeforeAll@AfterAll 方法之间共享测试实例状态。
  • @Nested 测试 classes.
  • @BeforeAll@AfterAll 方法的声明
  • 关于接口默认方法的 @BeforeAll@AfterAll 声明。
  • 使用 Kotlin 编程语言实现的测试 class 中 @BeforeAll@AfterAll 方法的简化声明。

有关详细信息,请参阅 test instance lifecycle 文档。

由于没有人提供合适的代码示例,我想给出一个简单的代码示例如下来理解这个概念,

每个方法示例 - Junit5 中的默认选项 注意两个方法是静态的,否则会引发异常,因为class在每个方法中实例化。

@TestInstance(Lifecycle.PER_METHOD)
public class MathUtilTestPerMethod {

    MathUtil util;

    @BeforeAll
    static void beforeAllInit() {
        System.out.println("running before all");
    }

    @AfterAll
    static void afterAllCleanUp() {
        System.out.println("running after all");
    }

    @BeforeEach
    void init() {
        util = new MathUtil();
        System.out.println("running before each...");
    }

    @AfterEach
    void cleanUp() {
        System.out.println("running after each...");
    }

    @Test
    void testSum() {
        assertEquals(2, util.addtwoNumbers(1, 1));
    }

}

每个 Class 样本 请注意,从这两个方法中删除了 static,并且 MathUtil 对象是全局创建的,而不是在方法中创建的,因为 class 仅实例化一次。

@TestInstance(Lifecycle.PER_CLASS)
public class MathUtilTestPerClass {

    MathUtil util = new MathUtil();

    @BeforeAll
    void beforeAllInit() {
        System.out.println("running before all");
    }

    @AfterAll
    void afterAllCleanUp() {
        System.out.println("running after all");
    }

    @BeforeEach
    void init() {
        System.out.println("running before each...");
    }

    @AfterEach
    void cleanUp() {
        System.out.println("running after each...");
    }

    @Test
    void testSum() {
        assertEquals(2, util.addtwoNumbers(1, 1));
    }

}

这在 Kotlin 中编写测试时也很有用,因为它没有静态方法。

因此,不要为 @BeforeAll@AfterAll 使用带有 @JvmStatic 函数的伴生对象,而是创建生命周期 PER_CLASS 并使用 [= 注释常规方法12=] 或 @AfterAll:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTest {

    @BeforeAll
    fun setup() {
        println("I am invoked only once")
    }
}

使用此方法时,请注意在必要时在 @BeforeEach@AfterEach 方法中重置实例变量。

感谢this article的帮助。