描述
我们有一个不断增长的应用程序,其中包含许多涉及到 redis 数据库连接的集成测试。由于数量不断增长,我们希望至少在类级别上将它们并行化。到目前为止,我们确实按顺序运行了所有测试,并com.github.kstyrc embedded-redis 0.6
在静态@BefroreClass
/@AfterClass
方法(jUnit 4)中启动(停止)了一个嵌入式 redis DB()。DB的端口始终相同-- 9736
。这也是在我们的 jedis 连接池的application.properties
via中设置的。spring.redis.port=9736
为了使并行化工作,我们必须动态获取我们的端口,并将其发布给连接工厂以进行连接池。一段时间后,我通过BeanPostProcessor
在配置中实施解决了这个问题。我剩下的问题是正确拦截 bean 生命周期和 Web 应用程序上下文。
代码片段并行测试
应用程序属性
...
spring.redis.port=${random.int[4000,5000]}
...
BeanPostProcessor
实施配置
@Configuration
public class TestConfig implements BeanPostProcessor {
private RedisServer redisServer;
private int redisPort;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (JedisConnectionFactory.class.equals(bean.getClass())) {
redisPort = ((JedisConnectionFactory) bean).getPort();
redisServer().start();
}
return bean;
}
@Bean(destroyMethod = "stop")
public RedisServer redisServer() {
redisServer = RedisServer.builder().port(redisPort).build();
return redisServer;
}
}
使用动态端口进行并行测试的启动和关闭
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class OfferControllerTest {
private MockMvc mockMvc;
@Inject
protected WebApplicationContext wac;
...
@Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(this.wac).apply(springSecurity()).build();
}
@After
public void tearDown() throws Exception {
offerRepository.deleteAll();
}
...
测试并行化是通过槽实现的maven-surefire-plugin 2.18.1
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<parallel>classes</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>
补充
发生的情况是,在 springs bean 初始化阶段,我们的 TestConfig 挂钩到 bean 的生命周期,并在连接池启动之前在JedisConnectionFactory
随机选择的端口上启动一个 redis 服务器。spring.redis.port=${random.int[4000,5000]}
由于 redisServer 本身是一个 bean,我们使用destroyMethod
bean 销毁时停止服务器,因此将其留给应用程序上下文生命周期。从静态端口到动态端口的从顺序到并行的过渡进展顺利。
问题
但是当我并行运行测试时,我会遇到如下错误:
java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@22b19d79 has been closed already
通过
@Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(this.wac).apply(springSecurity()).build();
}
并
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.redis-org.springframework.boot.autoconfigure.data.redis.RedisProperties': Initialization of bean failed; nested exception is java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@22b19d79 has been closed already
通过
@After
public void tearDown() throws Exception {
offerRepository.deleteAll();
}
帮助
我不太确定这个问题。也许我们可以省略 tearDown 调用, offerRepository.deleteAll()
因为@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
但是设置时的错误webAppContextSetup(this.wac).apply(springSecurity()).build()
仍然存在。
并行运行时应用程序上下文是否搞砸了,或者为什么设置中的应用程序上下文已经关闭?
我们是否选择了错误的方法(错误的模式)?如果是这样,我们应该改变什么?