java - 使用带有 JUnit5 扩展的测试容器
问题描述
我使用Spring boot
和JUnit5
。我的应用程序可与多个数据库一起使用:MySQL
、、Clickhouse
等。
对于集成测试,我创建了 JUnit5 Extensions:
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
扩展程序并使用随机端口。我的任务是通过简单地在该测试上方添加注释来为特定测试提升任何数据库。
解决方案
我有几个问题
- 它是否有效,您只想使用随机端口而不是固定端口,或者它根本不起作用?
- 我认为您对要求的要求太严格了。像在测试类/方法上放置一个注释一样简单并不是最终目的,因为您没有 API 来处理您的代码。据我了解,您无法访问扩展类中的任何内容(字段、方法)。我相信您可能会发现以下两种方法都更方便:
- 每个数据库容器都有一个单例工厂(变成
startContainerIfNeed()
工厂方法)并MySQLTestContainersExtension.getContainer()
在需要容器时调用 - 或者从扩展类中创建一个抽象类并在您的测试类中扩展它,在这种情况下,您可以保持
mariaDb
变量静态或使其成为常规字段并通过 getter 访问
在@Container
你的情况下也不做任何事情,你可以在这里阅读
推荐阅读
- c# - UWP C# - 如何使用 Windows.Data.Json 将 JsonObject 反序列化为一个类?
- android - 应用内帐单检查 skuid
- php - 在 php slim 3 中列出所有带有组的路由
- c++ - 函数局部枚举声明与 ADL 的交互
- python - 更改 Django 的 cookie 域设置会如何导致零星的 CSRF 失败?
- nginx - nginx SSL 连接失败
- python - python嵌入C++,函数返回shared_ptr(pybind11/boost_python)
- r - 匹配所有出现的术语 R 的正则表达式函数
- facebook - 如何使用 Facebook 登录配置 ASP.NET 身份 - 严格重定向
- angular - 如何使用 lodash _.filter() 过滤 Firestore 集合?