Junit 4 的 @Before 和 @BeforeClass 对比 Junit 5 @BeforeEach 和 @BeforeAll

概述

在本简短教程中,我们分别对 @Before@BeforeClass@BeforeEach@BeforeAll 注解来进行一些简短的说明和实践。

需要注意的是,针对 Junit 版本的不:

  • JUnit 4 对应使用的是: @Before@BeforeClass
  • JUnit 5 对应使用的是: @BeforeEach 和 *@BeforeAll

虽然名字有所改变,但是目的是相同的,并且功能都是向对应的。

另外,与其完全相对的还有一个就是 @After 的注解。

让我们从 JUnit 4 开始

@Before

这个注解是在 JUnit 4 中使用的。

使用这个注解的意思就是在测试类中,每一个测试开始执行之前都需要执行这个注解标记的方法。

通常用在我们希望对所有测试在执行之前都需要执行的方法。

让我们先对一些值进行初始化:

@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();
    }
}

请注意,在这里我们还在后面添加了一个 @After 注解,这个注解的意思是在每一个测试执行后都会对列表进行清理。

现在,我们添加一些测试来检查我们 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");
}

在本测试用例中:**针对每一个测试方法,确保所有的测试环境是相同的对每一个测试都非常重要。**在本用例中,我们主要需要确保变量的初始化是完全相同的,这是因为每一个测试方法在执行的时候都会对初始化后的变量进行修改。

随后,我们对输出的数据进行查看的时候,我们会发现针对每一个测试方法在执行的时候 initteardown 方法都会在测试执行之前执行一次。

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

@BeforeClass

针对每次测试执行的之前都要执行的方法相比,我们希望使用 @BeforeClass 这个注解。

这个注解的意思是针对测试类中的所有测试方法,只执行一次。

针对一些开销比较大的方法,你可能希望在所有方法执行之前只执行一次,比如说数据库连接和启动某个系统,这个时候你就可以使用 @BeforeClass 这个注解来执行标记的方法了。

让我们来创建一个与上面相同的测试类,不同的是我们使用了不同的注解。

@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");
    }
}

需要注意的是,**上面的这些方法必须是静态方法(static)**因为这些方法将会在 test 测试之前先行执行。

随后,让我们添加一些简单的测试:

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

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

这次,如果你查看测试方法的输出后,你会看到我们标记的方法只在所有测试开始执行之前执行了一次。

控制台的输出如下:

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

@BeforeEach@BeforeAll

@BeforeEac@BeforeAll 是 JUnit 5 中的注解,这个注解与 JUnit 4 中的 @Before@BeforeClass 是完全对应的。

这 2 个注解在 JUnit 5 中被重命名的原因主要是为了避免冲突。

你可以拷贝上面的方法,然后使用 JUnit 5 的注解来重新注解:
@BeforeEach@AfterEach

@RunWith(JUnitPlatform.class)
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();
    }

    // ...
}

然后你可以对程序的输出日志进行查看,你会看到与 @Before@After 注解是完全对应的:

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

最后,你可以重新注解下 @BeforeAll@AfterAll 再进行测试:

@RunWith(JUnitPlatform.class)
public class BeforeAllAndAfterAllAnnotationsUnitTest {

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

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

    // ...
}

随后通过测试来查看测试结果的输出:

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

结论

在本篇文章中,我们主要针对 @Before@BeforeClass@BeforeEach@BeforeAll 这几个注解进行了说明。

同时也针对这几个注解适用的 JUnit 版本进行了解释。

如果你使用 IDE 的话,在输入 Before 这个注解的时候,你会直观的发现这个几个注解分别是属于 JUnit 4 还是 5。

因为我们知道在 JUnit 5 的时候,JUnit 开始使用的包的名字为: org.junit.jupiter.api

上面的图,比较直观的反映了上面的情况。