我们的应用程序越来越多,其中涉及到与redis数据库连接的众多集成测试。由于数量不断增加,我们希望至少在课堂级别对它们进行并行化。
到目前为止,我们已经按顺序运行所有测试,并在静态com.github.kstyrc embedded-redis 0.6
/ @BefroreClass
方法(jUnit 4)中启动(停止)嵌入式redis数据库(@AfterClass
)。
数据库的端口始终相同 - 9736
。对于我们的jedis连接池,这也在application.properties
via spring.redis.port=9736
中设置。
为了使并行化工作,我们必须动态获取端口,并将其通告给连接工厂以进行连接池。
这个问题我在一段时间后通过在配置中实现BeanPostProcessor
得到了解决。我剩下的问题是正确拦截bean生命周期和Web应用程序上下文。
application.properties
...
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>
在spring bean初始化阶段,我们的TestConfig挂钩到JedisConnectionFactory
bean的生命周期,并在启动连接池之前通过spring.redis.port=${random.int[4000,5000]}
在随机选择的端口上启动redis服务器。由于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();
}
我不确定这个问题。也许我们可以省略对offerRepository.deleteAll()
的tearDown调用
因为@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
但是设置webAppContextSetup(this.wac).apply(springSecurity()).build()
的错误仍然存在。
并行运行时应用程序上下文是否被搞砸了?为什么设置中的应用程序上下文已经关闭? 我们选择了错误的方法(错误的模式)吗?如果是这样,我们应该改变什么?