1. 概述

本文将深入解析JUnit 4和5中@Before@BeforeClass@BeforeEach@BeforeAll注解的核心区别,并通过实际示例演示它们的使用场景。同时也会简要介绍其对应的@After系列注解。

2. @Before

核心特点@Before注解的方法会在每个测试方法执行前运行。当需要为每个测试执行通用初始化代码时特别实用。

以下示例展示了如何初始化一个列表并添加初始值:

@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注解的清理方法,确保每个测试后重置环境。

现在添加两个测试方法验证列表大小:

@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

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

⚠️ 注意:这些方法必须是静态的,因为它们在类实例化前执行。

添加简单测试验证:

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

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

日志显示setuptearDown在整个测试类生命周期中仅执行一次:

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

4. @BeforeEach 和 @BeforeAll

核心关系@BeforeEach@BeforeAll是JUnit 5中@Before@BeforeClass的等价注解。新命名更清晰地表达了执行时机。

4.1 @BeforeEach 示例

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

    // ...
}

日志输出与JUnit 4的@Before/@After完全一致:

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

4.2 @BeforeAll 示例

class BeforeAllAndAfterAllAnnotationsUnitTest {

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

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

    // ...
}

输出与JUnit 4的@BeforeClass/@AfterClass相同:

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

5. 总结

注解 执行时机 JUnit版本 典型场景
@Before 每个测试前 4 测试级别初始化
@BeforeClass 所有测试前(仅一次) 4 昂贵资源准备
@BeforeEach 每个测试前 5 测试级别初始化
@BeforeAll 所有测试前(仅一次) 5 昂贵资源准备

选择建议

  • 需要频繁重置的轻量级初始化 → @BeforeEach(JUnit 5)或@Before(JUnit 4)
  • 高成本的一次性准备操作 → @BeforeAll(JUnit 5)或@BeforeClass(JUnit 4)

完整示例代码可在GitHub仓库获取。


原始标题:@Before vs @BeforeClass vs @BeforeEach vs @BeforeAll