@TestInstance Annotation in JUnit 5 – JUnit 5中的@TestInstance注解

最后修改: 2019年 8月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Test classes often contain member variables referring to the system under test, mocks, or data resources used in the test. By default, both JUnit 4 and 5 create a new instance of the test class before running each test method. This provides a clean separation of state between tests.

测试类通常包含指向被测系统、模拟或测试中使用的数据资源的成员变量。默认情况下,JUnit 4和5在运行每个测试方法之前都会创建一个新的测试类实例。这为测试之间提供了干净的状态分离。

In this tutorial, we are going to learn how JUnit 5 allows us to modify the lifecycle of the test class using the @TestInstance annotation. We’ll also see how this can help us with managing large resources or more complex relationships between tests.

在本教程中,我们将学习JUnit 5如何允许我们使用@TestInstance注释来修改测试类的生命周期。我们还将看到这如何帮助我们管理大型资源或测试之间更复杂的关系。

2. Default Test Lifecycle

2.默认的测试生命周期

Let’s start by looking at the default test class lifecycle, common to JUnit 4 and 5:

让我们先来看看JUnit 4和5所共有的默认测试类的生命周期。

class AdditionTest {

    private int sum = 1;

    @Test
    void addingTwoReturnsThree() {
        sum += 2;
        assertEquals(3, sum);
    }

    @Test
    void addingThreeReturnsFour() {
        sum += 3;
        assertEquals(4, sum);
    }
}

This code could easily be JUnit 4 or 5 test code, apart from the missing public keyword that JUnit 5 does not require.

这段代码很容易成为JUnit 4或5的测试代码,除了缺少JUnit 5不需要的public关键字。

These tests pass because a new instance of AdditionTest is created before each test method is called. This means that the value of the variable sum is always set to 1 before the execution of each test.

这些测试通过了,因为在每个测试方法被调用之前,AdditionTest的一个新实例被创建。这意味着变量sum的值在执行每个测试之前总是被设置为1

If there were only one shared instance of the test object, the variable sum would retain its state after every test. As a result, the second test would fail.

如果只有一个测试对象的共享实例,变量sum将在每次测试后保留其状态。结果是,第二个测试会失败。

3. The @BeforeClass and @BeforeAll Annotations

3.@BeforeClass@BeforeAll注解

There are times when we need an object to exist across multiple tests. Let’s imagine we would like to read a large file to use as test data. Since it might be time-consuming to repeat that before every test, we might prefer to read it once and keep it for the whole test fixture.

有的时候我们需要一个对象存在于多个测试中。让我们想象一下,我们想读取一个大文件作为测试数据。由于在每次测试前重复这样做可能会很费时,我们可能更喜欢读取一次,并在整个测试夹具中保留它。

JUnit 4 addresses this with its @BeforeClass annotation:

JUnit 4通过其@BeforeClass注解解决了这个问题。

private static String largeContent;

@BeforeClass
public static void setUpFixture() {
    // read the file and store in 'largeContent'
}

We should note that we have to make the variables and the methods annotated with JUnit 4’s @BeforeClass static.

我们应该注意,我们必须使变量和方法用JUnit 4的@BeforeClass 静态注释。

JUnit 5 provides a different approach. It provides the @BeforeAll annotation which is used on a static function, to work with static members of the class.

JUnit 5提供了一种不同的方法。它提供了@BeforeAll注解,用于静态函数,以处理类的静态成员。

However, @BeforeAll can also be used with an instance function and instance members if the test instance lifecycle is changed to per-class.

然而,如果测试实例的生命周期改为per-class@BeforeAll也可以与实例函数和实例成员一起使用。

4. The @TestInstance Annotation

4.@TestInstance注释

The @TestInstance annotation lets us configure the lifecycle of JUnit 5 tests.

@TestInstance注解让我们配置JUnit 5测试的生命周期。

@TestInstance has two modes. One is LifeCycle.PER_METHOD (the default). The other is Lifecycle.PER_CLASS. The latter enables us to ask JUnit to create only one instance of the test class and reuse it between tests.

@TestInstance有两种模式。一种是LifeCycle.PER_METHOD(默认)。另一个是Lifecycle.PER_CLASS。后者使我们能够要求JUnit只创建一个测试类的实例,并在测试之间重复使用它。

Let’s annotate our test class with the @TestInstance annotation and use the Lifecycle.PER_CLASS mode:

让我们用@TestInstance注解来注释我们的测试类,并使用Lifecycle.PER_CLASS模式。

@TestInstance(Lifecycle.PER_CLASS)
class TweetSerializerUnitTest {

    private String largeContent;

    @BeforeAll
    void setUpFixture() {
        // read the file
    }

}

As we can see, none of the variables or functions are static. We are allowed to use an instance method for @BeforeAll when we use the PER_CLASS lifecycle.

我们可以看到,没有一个变量或函数是静态的。当我们使用PER_CLASS生命周期时,我们可以为@BeforeAll使用一个实例方法。

We should also note that the changes made to the state of the instance variables by one test will now be visible to the others.

我们还应该注意到,一个测试对实例变量的状态所做的改变,现在对其他测试也是可见的。

5. Uses of @TestInstance(PER_CLASS)

5.@TestInstance(PER_CLASS)的用途

5.1. Expensive Resources

5.1.昂贵的资源

This annotation is useful when instantiation of a class before every test is quite expensive. An example could be establishing a database connection, or loading a large file.

当每次测试前实例化一个类是相当昂贵的时候,这个注解是有用的。一个例子是建立一个数据库连接,或者加载一个大文件。

Solving this previously led to a complex mix of static and instance variables, which is now cleaner with a shared test class instance.

以前解决这个问题会导致静态变量和实例变量的复杂混合,现在用一个共享的测试类实例来解决这个问题就比较干净。

5.2. Deliberately Sharing State

5.2.故意分享国家

Sharing state is usually an anti-pattern in unit tests, but can be useful in integration tests. The per-class lifecycle supports sequential tests that intentionally share state. This may be necessary to avoid later tests having to repeat steps from earlier tests, especially if getting the system under test to the right state is slow.

共享状态在单元测试中通常是一种反模式,但在集成测试中可能很有用。每类生命周期支持有意共享状态的顺序测试。这对于避免后面的测试重复前面的测试步骤可能是必要的,特别是当被测系统进入正确状态的速度很慢时。

When sharing state, to execute all the tests in sequence, JUnit 5 provides us with the type-level @TestMethodOrder annotation. Then we can use the @Order annotation on the test methods to execute them in the order of our choice.

当共享状态时,为了依次执行所有的测试,JUnit 5为我们提供了类型级的@TestMethodOrder注解。然后我们可以在测试方法上使用@Order注解,按照我们选择的顺序执行它们。

@TestMethodOrder(OrderAnnotation.class)
class OrderUnitTest {

    @Test
    @Order(1)
    void firstTest() {
        // ...
    }

    @Test
    @Order(2)
    void secondTest() {
        // ...
    }

}

5.3. Sharing Some State

5.3.共享一些状态

The challenge with sharing the same instance of the test class is that some members may need to be cleaned between tests, and some may need to be maintained for the duration of the whole test.

共享测试类的同一实例的挑战是,有些成员可能需要在测试之间进行清理,有些成员可能需要在整个测试期间进行维护。

We can reset variables that need to be cleaned between tests with methods annotated with @BeforeEach or @AfterEach.

我们可以用@BeforeEach@AfterEach注释的方法在测试之间重置需要清理的变量。

6. Conclusion

6.结语

In this tutorial, we learned about the @TestInstance annotation and how it can be used to configure the lifecycle of JUnit 5 tests.

在本教程中,我们了解了@TestInstance注解以及如何使用它来配置JUnit 5测试的生命周期。

We also looked at why it might be useful to share a single instance of the test class, in terms of handling shared resources or deliberately writing sequential tests.

我们还研究了为什么共享测试类的单一实例可能是有用的,在处理共享资源或故意编写顺序测试方面。

As always, the code for this tutorial can be found on GitHub.

一如既往,本教程的代码可以在GitHub上找到