【问题标题】:Using Testcontainers with JUnit5 extensions使用带有 JUnit5 扩展的测试容器
【发布时间】:2021-03-07 01:39:56
【问题描述】:

我使用Spring bootJUnit5。我的应用适用于多个数据库:MySQLClickhouse 等。

为了集成测试,我创建了 JUnit5 扩展:

public class ClickHouseTestContainersExtension implements BeforeAllCallback, BeforeTestExecutionCallback {

    public static final String IMAGE_NAME = "registry.mycomp.com/db/clickhouse-server:20.5.3.27";

    @Container
    private static final FixedHostPortGenericContainer<?> clickHouse = new FixedHostPortGenericContainer<>(IMAGE_NAME)
            .withCopyFileToContainer(MountableFile.forClasspathResource("dbInitScripts/init_clickhouse.sql"), "/docker-entrypoint-initdb.d/init_clickhouse.sql")
            .withMinimumRunningDuration(Duration.ofSeconds(7))
            .withFixedExposedPort(8124, 8123);

    @Override
    public void beforeAll(ExtensionContext extensionContext) {
        startContainerIfNeed();
    }

    @Override
    public void beforeTestExecution(ExtensionContext extensionContext) {
        startContainerIfNeed();
    }

    public void startContainerIfNeed() {
        if (!clickHouse.isRunning()) {
            log.info("ClickHouse container is not running! Started.....");
            clickHouse.start();
        }
    }
}

和 MySQL:

public class MySQLTestContainersExtension implements BeforeAllCallback, BeforeTestExecutionCallback {

    private static final String IMAGE_NAME = "registry.mycomp.com/db/mariadb:10.4.11";

    @Container
    private static final FixedHostPortGenericContainer<?> mariaDb = new FixedHostPortGenericContainer<>(IMAGE_NAME)
            .waitingFor(new LogMessageWaitStrategy()
                    .withRegEx(".*ready for connections.*")
                    .withTimes(2)
                    .withStartupTimeout(Duration.of(3, MINUTES)))
            .withEnv("MYSQL_DATABASE", "db")
            .withEnv("MYSQL_USER", "sys")
            .withEnv("MYSQL_PASSWORD", "qwerty")
            .withEnv("MYSQL_ROOT_PASSWORD", "toor")
            .withEnv("MYSQL_RANDOM_ROOT_PASSWORD", "no")
            .withEnv("MYSQL_ALLOW_EMPTY_PASSWORD", "no")
            .withFixedExposedPort(33060, 3306)
            .withCopyFileToContainer(MountableFile.forClasspathResource("dbInitScripts/init_mysql.sql"),
                    "/docker-entrypoint-initdb.d/init_mysql.sql");

    @Override
    public void beforeAll(ExtensionContext extensionContext) {
        startContainerIfNeed();
    }

    @Override
    public void beforeTestExecution(ExtensionContext extensionContext) {
        startContainerIfNeed();
    }

    public void startContainerIfNeed() {
        if (!mariaDb.isRunning()) {
            log.info("MariaDB container is not running! Started.....");
            mariaDb.start();
        }
    }
}

还有许多其他类似的扩展。

我也有注释:

@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RUNTIME)
@ExtendWith({MySQLTestContainersExtension.class})
public @interface MySQL {
}

@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RUNTIME)
@ExtendWith({ClickHouseTestContainersExtension.class})
public @interface ClickHouse {
}

@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RUNTIME)
@ExtendWith({ClickHouseTestContainersExtension.class, MySQLTestContainersExtension.class,
        PostgreSQLTestContainersExtension.class, IgniteTestContainersExtension.class})
public @interface WithAllDatabases {
}

而且,当我编写一些测试时,我会使用这个扩展:


    @Test
    @ClickHouse
    @DisplayName("Test connection ClickHouse with alive status")
    void testConnectionClickHouseWithAliveStatus() throws IOException {
        //smth
    }

    @Test
    @MySQL
    @DisplayName("Test connection MySQL with alive status")
    void testConnectionMariaDBWithAliveStatus() throws IOException {
        //smth
    }

  //etc

我的连接属性,例如用户名、密码、url,存储在数据库中,而不是在 aplication.yml 中。我系统的任何用户都可以手动添加新连接。

所以我不能只使用像@DynamicPropertySource 这样的弹簧机制。

而且,我无法以任何方式从扩展中获取生成的随机端口!

因此,我使用一个固定端口来创建一组测试连接到测试中内容中提出的数据库!

告诉我是否有某种方法可以让您使用JUnit 5 扩展名并使用随机端口。我的任务是通过简单地在该测试上方添加注释来为特定测试提升任何数据库。

【问题讨论】:

    标签: java spring-boot junit5 testcontainers


    【解决方案1】:

    我有几个问题

    1. 它是否有效,而您只是想使用随机端口而不是固定端口,还是根本无效?
    2. 我认为你对要求的要求太严格了。就像在测试类/方法上放置一个注释一样简单并不是最终目的,因为您没有 API 来处理您的代码。据我了解,您无法访问扩展类中的任何内容(字段、方法)。 我相信您可能会发现以下任一方式更方便:
    • 每个数据库容器都有一个单例工厂(将startContainerIfNeed() 转换为工厂方法)并在需要容器时调用MySQLTestContainersExtension.getContainer()
    • 或从扩展类中创建一个抽象类并在您的测试类中扩展它,在这种情况下,您可以将mariaDb 变量保持为静态或使其成为常规字段并通过 getter 访问

    另外@Container 对你的情况没有任何作用,你可以阅读它here

    【讨论】:

      猜你喜欢
      • 2021-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-01
      • 2021-03-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多