2

描述

我们有一个不断增长的应用程序,其中包含许多涉及到 redis 数据库连接的集成测试。由于数量不断增长,我们希望至少在类级别上将它们并行化。到目前为止,我们确实按顺序运行了所有测试,并com.github.kstyrc embedded-redis 0.6在静态@BefroreClass/@AfterClass方法(jUnit 4)中启动(停止)了一个嵌入式 redis DB()。DB的端口始终相同-- 9736。这也是在我们的 jedis 连接池的application.propertiesvia中设置的。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,我们使用destroyMethodbean 销毁时停止服务器,因此将其留给应用程序上下文生命周期。从静态端口到动态端口的从顺序到并行的过渡进展顺利。

问题

但是当我并行运行测试时,我会遇到如下错误:
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()仍然存​​在。

并行运行时应用程序上下文是否搞砸了,或者为什么设置中的应用程序上下文已经关闭?
我们是否选择了错误的方法(错误的模式)?如果是这样,我们应该改变什么?

4

0 回答 0