You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit5测试类属性每次测试前为null的原因及非静态解决方法

问题解析与解决方案

为什么第二个测试中value是null?

JUnit 5的默认生命周期是**PER_METHOD**——说白了就是,每个测试方法执行时,都会创建一个全新的测试类实例。

你第一个测试setValueTest()是在第一个Junit5Test实例上把value设为"Test",但第二个测试readValueTest()运行时,JUnit会重新new一个完全新鲜的Junit5Test对象,这个新对象的value自然是默认的null,所以断言直接失败了。

除了静态变量,还有哪些处理方式?

1. 使用@TestInstance(Lifecycle.PER_CLASS)注解(最推荐)

这是最简单直接的方案,只需在测试类上添加这个注解,JUnit就会改成整个测试类只创建一个实例,所有测试方法共用同一个对象,属性状态自然就跟着共享了。

修改后的代码:

@TestMethodOrder(OrderAnnotation.class)
@TestInstance(Lifecycle.PER_CLASS) // 加上这个注解就行
class Junit5Test {
    private String value;

    @Test
    @Order(1)
    void setValueTest() {
        this.value = "Test";
        assertNotNull(this.value);
    }

    @Test
    @Order(2)
    void readValueTest() {
        assertNotNull(this.value); // 现在会成功,因为是同一个实例的属性
    }
}

2. 使用ExtensionContext.Store存储共享状态(灵活进阶)

如果需要更灵活的状态共享(比如跨多个测试类或者复杂场景),可以利用JUnit 5的扩展机制,通过ExtensionContext.Store来存取值。

示例代码(自定义扩展+使用Store):

// 自定义扩展,负责存储和读取共享值
public class SharedValueExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        // 可在这里做初始化操作
    }

    @Override
    public void afterTestExecution(ExtensionContext context) throws Exception {
        // 把当前测试的value存入Store
        context.getStore(ExtensionContext.Namespace.create(Junit5Test.class))
               .put("sharedValue", ((Junit5Test) context.getTestInstance().get()).value);
    }
}

// 测试类绑定扩展
@TestMethodOrder(OrderAnnotation.class)
@ExtendWith(SharedValueExtension.class)
class Junit5Test {
    private String value;

    @Test
    @Order(1)
    void setValueTest() {
        this.value = "Test";
        assertNotNull(this.value);
    }

    @Test
    @Order(2)
    void readValueTest(ExtensionContext context) {
        // 从Store里取出之前存的共享值
        String sharedValue = context.getStore(ExtensionContext.Namespace.create(Junit5Test.class))
                                   .get("sharedValue", String.class);
        assertNotNull(sharedValue);
    }
}

3. 重构测试逻辑,避免状态共享(最佳实践)

其实从测试设计的角度,每个测试方法应该是独立、无状态的,依赖共享状态容易导致测试耦合,后期维护起来很麻烦。如果可以的话,建议重构测试:

  • 每个测试方法自己初始化所需的状态
  • 或者把通用逻辑提取到@BeforeEach方法中,确保每个测试都有干净的初始环境

比如这样调整:

@TestMethodOrder(OrderAnnotation.class)
class Junit5Test {
    private String value;

    @BeforeEach
    void setUp() {
        // 可在这里设置默认值,按需调整
    }

    @Test
    @Order(1)
    void setValueTest() {
        this.value = "Test";
        assertNotNull(this.value);
    }

    @Test
    @Order(2)
    void readValueTest() {
        // 自己初始化需要的值,不依赖其他测试的状态
        this.value = "Test";
        assertNotNull(this.value);
    }
}

为什么不推荐用静态变量?

静态变量属于类而非实例,会在整个JVM生命周期中存在,很容易造成测试污染——比如某个测试修改了静态变量的值,会影响后续所有测试(包括其他测试类的测试),导致测试结果不可靠。除非是常量,否则尽量别用静态变量来共享测试状态。

内容的提问来源于stack exchange,提问作者Elmar Brauch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:13:05