2

説明

Redis DB への接続を伴う多数の統合テストを伴う成長中のアプリケーションがあります。数が増えているため、少なくともクラスレベルでそれらを並列化したいと考えています。これまで、すべてのテストを順番に実行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>

補足

何が起こるかというと、Spring Bean の初期化フェーズ中に、接続プールが開始される前にJedisConnectionFactory、TestConfig が Bean のライフサイクルにフックし、ランダムに選択されたポートで 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()は依然として残ります。

並行して実行しているときにアプリケーション コンテキストが台無しになりましたか、それともセットアップのアプリケーション コンテキストが既に閉じられているのはなぜですか?
間違ったアプローチ (間違ったパターン) を選択しましたか? もしそうなら、私たちは何を変えるべきですか?

4

0 に答える 0