@Before vs @BeforeClass vs @BeforeEach vs @BeforeAll – @Before vs @BeforeClass vs @BeforeEach vs @BeforeAll

最后修改: 2018年 4月 15日

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

1. Overview

1.概述

In this short tutorial, we’re going to explain the differences between the @Before, @BeforeClass, @BeforeEach and @BeforeAll annotations in JUnit 4 and 5 — with practical examples of how to use them.

在这个简短的教程中,我们将解释JUnit 4和5中的@Before@BeforeClass@BeforeEach@BeforeAll注解之间的区别–用实际例子说明如何使用它们。

We’ll also briefly cover their @After complementary annotations.

我们还将简要介绍他们的@After补充注释。

Let’s start with JUnit 4.

让我们从JUnit 4开始。

2. @Before

2.@Before

Methods annotated with the @Before annotation are run before each test. This is useful when we want to execute some common code before running a test.

@Before注解的方法在每个测试前运行。当我们想在运行测试前执行一些常用代码时,这很有用。

Let’s initialize a list and add some values:

让我们初始化一个列表并添加一些值。

@RunWith(JUnit4.class)
public class BeforeAndAfterAnnotationsUnitTest {

    // ...

    private List<String> list;

    @Before
    public void init() {
        LOG.info("startup");
        list = new ArrayList<>(Arrays.asList("test1", "test2"));
    }

    @After
    public void teardown() {
        LOG.info("teardown");
        list.clear();
    }
}

Notice that we also added another method annotated with @After in order to clear the list after the execution of each test.

注意,我们还添加了另一个用@After注解的方法,以便在每个测试执行后清除列表。

Now let’s add some tests to check the size of our list:

现在让我们添加一些测试来检查我们列表的大小。

@Test
public void whenCheckingListSize_thenSizeEqualsToInit() {
    LOG.info("executing test");
    assertEquals(2, list.size());

    list.add("another test");
}

@Test
public void whenCheckingListSizeAgain_thenSizeEqualsToInit() {
    LOG.info("executing another test");
    assertEquals(2, list.size());

    list.add("yet another test");
}

In this case, it’s crucial to make sure that the test environment is properly set up before running each test since the list is modified during every test execution.

在这种情况下,确保在运行每个测试之前正确设置测试环境是至关重要的,因为在每次测试执行过程中都会修改列表。

If we take a look at the log output, we can check that the init and teardown methods were run once per test:

如果我们看一下日志输出,我们可以检查到initteardown方法在每个测试中运行一次。

... startup
... executing another test
... teardown
... startup
... executing test
... teardown

3. @BeforeClass

3.@BeforeClass

When we want to execute an expensive common operation before each test, it’s preferable to execute it only once before running all tests using @BeforeClass.

当我们想在每个测试前执行一个昂贵的普通操作时,最好是在运行所有测试前使用@BeforeClass只执行一次。

Some examples of common expensive operations are the creation of a database connection or the startup of a server.

常见的昂贵操作的一些例子是创建一个数据库连接或启动一个服务器。

Let’s create a simple test class that simulates the creation of a database connection:

让我们创建一个简单的测试类,模拟创建一个数据库连接。

@RunWith(JUnit4.class)
public class BeforeClassAndAfterClassAnnotationsUnitTest {

    // ...
    
    @BeforeClass
    public static void setup() {
        LOG.info("startup - creating DB connection");
    }

    @AfterClass
    public static void tearDown() {
        LOG.info("closing DB connection");
    }
}

Notice that these methods have to be static, so they’ll be executed before running the tests of the class.

注意,这些方法必须是静态的,所以它们将在运行类的测试之前被执行。

As before, let’s also add some simple tests:

和以前一样,我们也来添加一些简单的测试。

@Test
public void simpleTest() {
    LOG.info("simple test");
}

@Test
public void anotherSimpleTest() {
    LOG.info("another simple test");
}

This time, if we take a look at the log output, we can check that the setup and tearDown methods were run only once:

这一次,如果我们看一下日志输出,我们可以检查到setuptearDown方法只运行了一次。

... startup - creating DB connection
... simple test
... another simple test
... closing DB connection

4. @BeforeEach and @BeforeAll

4.@BeforeEach@BeforeAll

@BeforeEach and @BeforeAll are the JUnit 5 equivalents of @Before and @BeforeClass. These annotations were renamed with clearer names to avoid confusion.

@BeforeEach和@BeforeAll是JUnit 5对应的@Before@BeforeClass这些注解被重新命名,名称更清晰,以避免混淆。

Let’s duplicate our previous classes using these new annotations, starting with the @BeforeEach and @AfterEach annotations:

让我们使用这些新注解来复制我们之前的类,从@BeforeEach@AfterEach注解开始。

class BeforeEachAndAfterEachAnnotationsUnitTest {

    // ...
    
    private List<String> list;
    
    @BeforeEach 
    void init() {
        LOG.info("startup");
        list = new ArrayList<>(Arrays.asList("test1", "test2"));
    }

    @AfterEach
    void teardown() {
        LOG.info("teardown");
        list.clear();
    }

    // ...
}

If we check logs, we can confirm that it works in the same way as with the @Before and @After annotations:

如果我们检查日志,我们可以确认它与@Before@After注释的工作方式相同。

... startup
... executing another test
... teardown
... startup
... executing test
... teardown

Finally, let’s do the same with the other test class to see the @BeforeAll and @AfterAll annotations in action:

最后,让我们对另一个测试类做同样的事情,看看@BeforeAll@AfterAll注释的作用。

public class BeforeAllAndAfterAllAnnotationsUnitTest {

    // ...
    
    @BeforeAll
    public static void setup() {
        LOG.info("startup - creating DB connection");
    }

    @AfterAll
    public static void tearDown() {
        LOG.info("closing DB connection");
    }

    // ...
}

And the output is the same as with the old annotation:

而输出结果与旧的注释相同。

... startup - creating DB connection
... simple test
... another simple test
... closing DB connection

5. Conclusion

5.结论

In this article, we showed the differences between the @Before, @BeforeClass, @BeforeEach and @BeforeAll annotations in JUnit and when each of them should be used.

在这篇文章中,我们展示了JUnit中@Before@BeforeClass@BeforeEach@BeforeAll注解之间的区别以及何时应该使用它们。

As always, the full source code of the examples is available over on GitHub.

一如既往,这些示例的完整源代码可在GitHub上获得