Я использовал Spring boot
и JUnit5
. Мое приложение работает с несколькими базами данных: MySQL
, Clickhouse
и т. Д.
Для интеграционного тестирования я создал расширения 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-адрес, хранятся в базе данных, а не в apllication.yml. Любой пользователь моей системы может вручную добавить новое соединение.
Так что я не могу просто использовать пружинный механизм, такой как @DynamicPropertySource
.
Более того, я никак не могу получить сгенерированный случайный порт из расширения!
Поэтому я использую фиксированный порт для создания набора тестовых подключений к базе данных, созданной в содержимом тестов!
Подскажите, есть ли какой-нибудь подход, который позволит вам использовать расширение JUnit 5
и использовать случайный порт. Моей задачей было поднять любую базу данных для конкретного теста, просто поместив аннотацию над этим тестом.