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
相关产品推荐
相关产品推荐

