1. Что такое тест-контейнеры?
Testcontainers — это библиотека, которая предоставляет простой и легкий API для начальной загрузки локальной разработки и тестирования зависимостей, инкапсулируя реальные сервисы в контейнеры Docker. Используя Testcontainers, вы можете писать тесты, основанные на тех же сервисах, которые вы используете в рабочей среде, без необходимости использования макетов или сервисов в памяти.
Говоря более простыми словами, этоtestcontainers Он позволяет запускать Docker-контейнер через язык программирования и автоматически закрывать контейнер после завершения тестирования программы.
2. Каковы преимущества тестконтейнеров?
3. На что следует обратить внимание при использовании тест-контейнеров?
4. Стратегия подключения тестконтейнеров к докеру
Тестовые контейнеры во время выполнения попытаются подключиться, используя следующие стратегии в следующем порядке: Docker Демон:
Переменные среды: – DOCKER_HOST – DOCKER_TLS_VERIFY – DOCKER_CERT_PATH
Роль каждой переменной:
значение по умолчанию – DOCKER_HOST=https://localhost:2376 – DOCKER_TLS_VERIFY=1 – DOCKER_CERT_PATH=~/.docker
Мы можем изменить приведенные выше значения с помощью переменной среды, Пример
System.setProperty("DOCKER_HOST","tcp://192.168.0.1:2375")
Примечание: Путем модификации программы мы должны убедиться, что System.setProperty установлен до того, как Testcontainers запустит контейнер, иначе он не вступит в силу.
Вышеуказанный контент можно найти на официальном сайте.https://java.testcontainers.org/supported_docker_environment/Найти более подробное введение
Ниже приведена демонстрация использования тест-контейнеров, интегрирующих Redis и модульное тестирование через Junit5 в качестве примера.
1. Внедряем junit5 gav в pom проекта.
<properties>
<junit-platform.version>1.9.2</junit-platform.version>
<junit-jupiter.version>5.9.2</junit-jupiter.version>
</properties>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-commons</artifactId>
<version>${junit-platform.version}</version>
<scope>test</scope>
</dependency>
Примечание: Если вы используете более позднюю версию Springboot, вы можете импортировать ее напрямую.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-commons</artifactId>
<version>${junit-platform.version}</version>
<scope>test</scope>
</dependency>
Вот и все
2. Внедрить тест-контейнеры gav в pom в проекте.
<properties>
<testcontainers.version>1.17.3</testcontainers.version>
</properties>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
Конечно, нам также нужно представить клиент redis gav, потому что это должны знать все, поэтому я не буду его вводить.
3. В нашем модульном тесте позвольте testcontainers запустить контейнер Redis.
Пример кода выглядит следующим образом
@Container
private static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:6.2.6"))
.withExposedPorts(6379);
Приведенный выше код означает создание зеркала в виде контейнера redis:6.2.6 и предоставление порта 6379.
Также на тестовом занятии,Нужно добавить@Testcontainers(disabledWithoutDocker = true)
аннотация
@Testcontainers(disabledWithoutDocker = true)
public class RedisTest {
@Container
private static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:6.2.6"))
.withExposedPorts(6379);
}
4. Интегрируйте наши бизнес-программы с контейнерами.
private Jedis jedis;
@BeforeEach
public void setUp() {
int port = redis.getMappedPort(6379);
jedis = new Jedis(redis.getHost(), port);
}
5. Запустите модульные тесты
@Testcontainers(disabledWithoutDocker = true)
public class RedisTest {
@Container
private static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:6.2.6"))
.withExposedPorts(6379);
private Jedis jedis;
@BeforeEach
public void setUp() {
int port = redis.getMappedPort(6379);
jedis = new Jedis(redis.getHost(), port);
}
@AfterEach
public void tearDown() {
if (jedis != null) {
jedis.close();
}
}
@Test
public void testRedisConnectionAndSetAndGet() {
// Тестовое соединение и простой доступ
String key = "testKey";
String value = "testValue";
jedis.set(key, value);
String result = jedis.get(key);
assert result.equals(value);
}
Сначала мы можем наблюдать за Docker-контейнером и обнаружить, что Redis-контейнер успешно запущен.
Давайте еще раз посмотрим на результаты модульного теста. Они такие же, как мы и ожидали.
После завершения модульного теста давайте посмотрим на контейнер.
Обнаружено, что контейнер уничтожен
Приведенные выше примеры также содержат подробные руководства на официальном сайте, вы можете проверить следующую ссылку. https://java.testcontainers.org/quickstart/junit_5_quickstart/
В настоящее время наши проекты в основном интегрированы с Springboot. Далее мы кратко продемонстрируем интеграцию тестовых контейнеров, Springboot и Redis.
Полный пример выглядит следующим образом
@SpringBootTest(classes = TestcontainersApplication.class,webEnvironment = SpringBootTest.WebEnvironment.NONE)
@Testcontainers(disabledWithoutDocker = true)
public class RedisContainerByDynamicPropertySourceTest {
@Autowired
private StringRedisTemplate redisTemplate;
@Container
private static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:6.2.6"))
.withExposedPorts(6379);
// @BeforeEach
// public void setUp() {
//
// System.setProperty("spring.redis.host", redis.getHost());
// System.setProperty("spring.redis.port", redis.getMappedPort(6379).toString());
// }
/**
* Spring TEST DynamicPropertySource был представлен в версии 5.2.5.
* @param registry
*/
@DynamicPropertySource
private static void registerRedisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.redis.host", redis::getHost);
registry.add("spring.redis.port", () -> redis.getMappedPort(6379)
.toString());
}
@Test
public void testRedisConnectionAndSetAndGet() {
// Тестовое соединение и простой доступ
String key = "testKey";
String value = "testValue";
redisTemplate.opsForValue().set(key, value);
String result = redisTemplate.opsForValue().get(key);
assert Objects.equals(result, value);
}
}
Основной код
@DynamicPropertySource
private static void registerRedisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.redis.host", redis::getHost);
registry.add("spring.redis.port", () -> redis.getMappedPort(6379)
.toString());
}
Эта аннотация доступна только после весенней версии 5.2.5.,Когда вы заранее не знаете значение атрибута,проходить@DynamicPropertySourceиDynamicPropertyRegistry Совместное размещение может реализовать динамическую привязку атрибутов. Подробное описание можно найти на официальном сайте Spring. https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/ctx-management/dynamic-property-sources.html
Примечание: Если версия Springboot относительно низкая, вам необходимо ввести следующий gav в pom проекта, чтобы использовать DynamicPropertySource
<spring.version>5.2.15.RELEASE</spring.version>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
Просмотр результатов модульного теста
Примечание: Поскольку мое окно не поддерживает виртуализацию, я не могу установить Docker. рабочий стол. Поэтому мой Пример подключен к удаленному серверу для тестирования.
Поскольку вы хотите подключиться к удаленному докер-серверу, необходимо открыть порт 2375. Шаги для включения следующие:
vim /usr/lib/systemd/system/docker.service
изменить значение по умолчанию
ExecStart=/usr/bin/dockerd -H unix://var/run/docker.sock \
Измените следующим образом
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
Просто добавьте его напрямую,В Интернете много чего написано
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock \
Написание таким образом приведет к ошибке. После модификации выполните
systemctl daemon-reload
service docker restart
проходить
ps -ef | grep docker
Проверьте, открыт ли порт 2375
Друзья, которые были заминированы, знают, что многие хосты считаются майнинговыми машинами, поскольку порт 2375 открыт в общедоступной сети. Таким образом, инструмент applyssh может создать туннель и обеспечить доступ к туннелю. Пример
Однако именно из-за прохождения здесь доступа к туннелю он очень затруднителен в дальнейшем.
Начните объяснять подводные камни
Яма 1: тестовые контейнеры не могут подключиться к удаленному докеру
Сначала я был подвергнут
System.setProperty("DOCKER_HOST","tcp://192.168.0.1:2375")
Настроил, потому что точка, которую я установил, позже времени, когда Testcontainers создал контейнер, поэтому Testcontainers подключились к локальному докеру. Поскольку я не устанавливал докер локально, я не смог подключиться.
мы можемпроходитьсуществоватьideaначальствонастраивать
Но есть блогер, который еще лучше. Он напрямую модифицирует код. Измените содержимое кода следующим образом.
Примечание: В проекте необходимо представить следующий GAV
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.2.13</version>
</dependency>
/**
* Стратегия пользовательского подключения Docker для testContainer
*/
@Slf4j
public class MyDockerClientProviderStrategy extends DockerClientProviderStrategy {
private final DockerClientConfig dockerClientConfig;
private static final String DOCKER_HOST = "tcp://127.0.0.1:2375";
/**
* Настраиваем dockerClientConfig во время инициализации, используем docker-java для подключения к докеру
*/
public MyDockerClientProviderStrategy() {
DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder();
configBuilder.withDockerHost(DOCKER_HOST);
//проход настраивается следующим образом: закрываем RYUK и решаем Could not connect to Ryuk at localhost
System.setProperty("TESTCONTAINERS_RYUK_DISABLED","true");
// // Включить проверку dockerTLS
// configBuilder.withDockerTlsVerify(true);
// // Измените папку, в которой находится ключ, на каталог вашего проекта. и все
// configBuilder.withDockerCertPath("C:\\Users\\Administrator\\Desktop\\docker");
dockerClientConfig = configBuilder.build();
}
/**
* Определите конфигурацию подключения докера здесь
*/
@Override
public TransportConfig getTransportConfig() {
return TransportConfig.builder()
.dockerHost(dockerClientConfig.getDockerHost())
.sslConfig(dockerClientConfig.getSSLConfig())
.build();
}
/**
* В соответствии со вторым фильтром выше, он всегда возвращает trueВот. и все。
*/
@Override
protected boolean isApplicable() {
return true;
}
@Override
public String getDescription() {
return "my-custom-strategy";
}
}
Создайте META-INF/services/org.testcontainers.dockerclient.DockerClientProviderStrategy в src/main/resources. Содержимое файла следующее
com.github.lybgeek.testcontainers.MyDockerClientProviderStrategy
По сути, это механизм spi. Содержание статьи блогера следующее. Друзья, кому интересно, могут ее прочитать. https://blog.csdn.net/LHFFFFF/article/details/127117917
Причина: Не удалось подключиться к Рюку на локальном хосте.
Я не знаю, что это такое, это официальная информация. https://github.com/testcontainers/testcontainers-java/issues/3609#issuecomment-769615098 настраивать
TESTCONTAINERS_RYUK_DISABLED=true
Отключить РЮК
Отключение, похоже, не дает никакого эффекта.
Пример: истекло время ожидания открытия порта контейнера (порты локального хоста: [] должны прослушиваться)
Сначала я был подвергнутдоступ к туннелю,Позже я обнаружил, что каждый раз, когда я начинаю,Порт контейнера, созданного testcontainer, изменится. Пример
Например, порт 32788.,После перезапуска оно станет 32789. Позже я создам случайную группу безопасности порта.,Например, разрешите доступ к сегменту порта 30000-40000. Так что проблема временно решена
Эта статья — всего лишь введение. На официальном сайте Testcontainers есть более подробные примеры. Если вам интересно, вы можете ознакомиться с ними. https://testcontainers.com/guides/
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-testcontainers