如何将 Testcontainers 与 @DataJpaTest 结合起来避免代码重复?

问题描述

我想使用 JUnit 5 将测试容器与 @DataJpaTest(和 @SpringBoottest)一起使用。我使用 @Testcontainers@Container 注释进行基本设置,如下所示:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgresqlContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.assertj.core.api.Assertions.assertthat;

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
public class AtleteRepositoryTest {
    @Container
    private static final PostgresqlContainer<?> CONTAINER = new PostgresqlContainer<>("postgres:11");

    @DynamicPropertySource
    static void registerProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url",CONTAINER::getJdbcUrl);
        registry.add("spring.datasource.username",CONTAINER::getUsername);
        registry.add("spring.datasource.password",CONTAINER::getpassword);
    }

    @Autowired
    private AtleteRepository repository;

    @Test
    void testSave() {
        repository.save(new Atlete("Wout Van Aert",1,0));

        assertthat(repository.count()).isEqualTo(1);
    }
}

有关完整示例代码https://github.com/wimdeblauwe/blog-example-code/tree/feature/testcontainers-datajpatest/testcontainers-datajpatestAtleteRepositoryTestTeamRepositoryTest),请参阅 TestcontainersDatajpatestApplicationTests

为了避免重复声明 Postgresql 容器和动态属性,我尝试了以下操作:

JUnit 5 扩展

Baeldung 有一个关于如何use a JUnit 5 extension to avoid the duplication博客

import org.junit.jupiter.api.extension.AfterallCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.testcontainers.containers.PostgresqlContainer;

public class PostgresqlExtension implements BeforeAllCallback,AfterallCallback {

    private PostgresqlContainer<?> postgres;

    @Override
    public void beforeAll(ExtensionContext context) {
        postgres = new PostgresqlContainer<>("postgres:11");

        postgres.start();
        System.setProperty("spring.datasource.url",postgres.getJdbcUrl());
        System.setProperty("spring.datasource.username",postgres.getUsername());
        System.setProperty("spring.datasource.password",postgres.getpassword());
    }

    @Override
    public void afterall(ExtensionContext context) {
        postgres.stop();
    }
}

如果您只有 1 个测试,它会起作用,但如果您同时运行多个测试(使用 IntelliJ 或使用 Maven),则在这种情况下,其中一项测试将失败,因为无法建立与数据库的连接。 另请注意,此扩展使用 DynamicPropertyRegistry,而是使用普通的环境变量。 查看代码feature/testcontainers-datajpatest_baeldung-extension 分支。

使用通用超类

在分支 feature/testcontainers-datajpatest_database-base-test 上,我尝试使用通用超类:


import org.junit.jupiter.api.Afterall;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgresqlContainer;

public class DatabaseBaseTest {
    private static final PostgresqlContainer<?> CONTAINER = new PostgresqlContainer<>("postgres:11");

    @BeforeAll
    static void start() {
        CONTAINER.start();
    }

    @Afterall
    static void stop() {
        CONTAINER.stop();
    }

    @DynamicPropertySource
    static void registerProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url",() -> {
            String jdbcUrl = CONTAINER.getJdbcUrl();
            System.out.println("jdbcUrl = " + jdbcUrl);
            return jdbcUrl;
        });
        registry.add("spring.datasource.username",CONTAINER::getpassword);
    }
}

不幸的是,这也不起作用。我在日志中注意到 @DynamicPropertySource 注释的方法调用一次,而不是每次测试时调用,这让我尝试了选项 3:

子类中有 @DynamicPropertySource 的公共超类

当使用公共超类,但在每个子类中添加@DynamicPropertySource方法时,它再次起作用。

此类子类的示例代码

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class AtleteRepositoryTest extends DatabaseBaseTest {

    @DynamicPropertySource
    static void registerProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url",0));

        assertthat(repository.count()).isEqualTo(1);
    }
}

请参阅该版本的分支 feature/testcontainers-datajpatest_database-base-test_subclasses

所以虽然它有效,但每个测试类中仍然有很多重复。

是否有其他选择可以避免重复?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)