如何在JUnit 5中为测试隔离初始化Spring Bean?
如何在JUnit 5 + Spring测试中实现每个测试方法重新初始化Spring Bean
在JUnit 5和Spring测试场景中,我们希望每个测试用例都能使用全新的Spring Bean实例,避免不同测试间的状态污染。
JUnit 5默认会在每个测试方法执行前创建新的测试类实例,因此普通实例变量能自动实现隔离,比如下面的代码可以正常运行:
public class TestInstanceVaribale{ int number = 0; @Test public void test1() { number += 3; Assertions.assertEquals(3, number); } @Test public void test2() { number += 5; Assertions.assertEquals(5, number); } }
但Spring Bean默认是单例模式,容器会复用同一个实例,导致测试间互相影响。比如下面的代码执行会失败:
Car类
@Component public class Car { public String name = "myCar"; }
测试类
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Order; @SpringBootTest @TestMethodOrder(OrderAnnotation.class) public class TestSpringVariable { @Autowired Car car; @Test @Order(1) public void test1() { car.name = "testCar"; Assertions.assertEquals("testCar", car.name); } @Test @Order(2) public void test2() { // 执行失败:预期值是myCar,实际值是testCar Assertions.assertEquals("myCar", car.name); } }
我们期望实现类似这样的效果:每个测试方法都重新初始化Spring Bean:
@SpringBootTest // 期望有类似@InitializeSpringBeanPerMethod的注解 public class TestSpringVariable2 { @Autowired Car car; @BeforeEach public void initialize() { // 期望有类似SpringBean.initialize()的方法重置Bean } }
解决方案
方法一:使用@DirtiesContext刷新整个Spring上下文
通过@DirtiesContext标记测试方法执行后刷新上下文,确保每个测试方法都使用全新的容器和Bean实例:
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.MethodMode; @SpringBootTest @TestMethodOrder(OrderAnnotation.class) public class TestSpringVariable { @Autowired Car car; @Test @Order(1) @DirtiesContext(methodMode = MethodMode.AFTER_METHOD) public void test1() { car.name = "testCar"; Assertions.assertEquals("testCar", car.name); } @Test @Order(2) @DirtiesContext(methodMode = MethodMode.AFTER_METHOD) public void test2() { Assertions.assertEquals("myCar", car.name); } }
methodMode = MethodMode.AFTER_METHOD表示每个测试方法执行完成后,Spring上下文会被标记为"脏"并重新创建,下一个测试方法会使用全新的上下文和Bean。
方法二:将Bean改为prototype作用域,配合@BeforeEach重新获取
如果只需要特定Bean实现隔离,无需刷新整个上下文,可以将Bean的作用域改为prototype,并在每个测试前重新获取实例:
修改Car类
import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class Car { public String name = "myCar"; }
测试类
import org.springframework.context.ApplicationContext; @SpringBootTest @TestMethodOrder(OrderAnnotation.class) public class TestSpringVariable2 { @Autowired ApplicationContext context; private Car car; @BeforeEach public void init() { // 每次测试前从容器获取新的Car实例 car = context.getBean(Car.class); } @Test @Order(1) public void test1() { car.name = "testCar"; Assertions.assertEquals("testCar", car.name); } @Test @Order(2) public void test2() { Assertions.assertEquals("myCar", car.name); } }
prototype作用域的Bean每次从容器获取时都会创建新实例,因此@BeforeEach中重新获取就能保证每个测试用例拿到的是全新的Bean。
内容的提问来源于stack exchange,提问作者bittap
相关产品推荐
相关产品推荐

