Перейти к основному содержимому

Тестовые контейнеры Docker в тестах Java

· 4 мин. чтения

1. Введение

В этом руководстве мы рассмотрим библиотеку Java TestContainers . Это позволяет нам использовать контейнеры Docker в наших тестах. В результате мы можем писать автономные интеграционные тесты, зависящие от внешних ресурсов.

Мы можем использовать в наших тестах любой ресурс, у которого есть образ докера. Например, есть изображения для баз данных, веб-браузеров, веб-серверов и очередей сообщений. Поэтому мы можем запускать их как контейнеры в наших тестах.

2. Требования

Библиотека TestContainers может использоваться с Java 8 и выше. Кроме того, он совместим с JUnit Rules API.

Во-первых, давайте определим зависимость maven для основных функций:

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.11.4</version>
</dependency>

Также есть модули для специализированных контейнеров. В этом руководстве мы будем использовать PostgreSQL и Selenium.

Добавим соответствующие зависимости:

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql </artifactId>
<version>1.11.4</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>selenium </artifactId>
<version>1.11.4</version>
</dependency>

Мы можем найти последние версии на Maven Central .

Также нам нужен Docker для запуска контейнеров . Инструкции по установке см. в документации Docker .

Убедитесь, что вы можете запускать контейнеры Docker в своей тестовой среде.

3. Использование

Давайте настроим общее правило контейнера:

@ClassRule
public static GenericContainer simpleWebServer
= new GenericContainer("alpine:3.2")
.withExposedPorts(80)
.withCommand("/bin/sh", "-c", "while true; do echo "
+ "\"HTTP/1.1 200 OK\n\nHello World!\" | nc -l -p 80; done");

Мы создаем тестовое правило GenericContainer , указав имя образа докера. Затем мы настраиваем его с помощью методов билдера:

  • Мы используем withExposedPorts , чтобы открыть порт из контейнера.
  • withCommand определяет команду контейнера. Он будет выполнен при запуске контейнера.

Правило снабжено аннотацией @ClassRule. В результате он запустит контейнер Docker до запуска любого теста в этом классе . Контейнер будет уничтожен после выполнения всех методов.

Если вы примените аннотацию @Rule , правило GenericContainer запустит новый контейнер для каждого метода тестирования. И он остановит контейнер, когда этот метод тестирования завершится.

Мы можем использовать IP-адрес и порт для связи с процессом, запущенным в контейнере :

@Test
public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
throws Exception {
String address = "http://"
+ simpleWebServer.getContainerIpAddress()
+ ":" + simpleWebServer.getMappedPort(80);
String response = simpleGetRequest(address);

assertEquals(response, "Hello World!");
}

4. Режимы использования

Существует несколько режимов использования тестовых контейнеров. Мы видели пример запуска GenericContainer.

В библиотеке TestContainers также есть определения правил со специальной функциональностью. Они предназначены для контейнеров общих баз данных, таких как MySQL, PostgreSQL; и другие, такие как веб-клиенты.

Хотя мы можем запускать их как общие контейнеры, специализации предоставляют расширенные удобные методы.

4.1. Базы данных

Предположим, нам нужен сервер базы данных для интеграционных тестов уровня доступа к данным. Мы можем запускать базы данных в контейнерах с помощью библиотеки TestContainers.

Например, мы запускаем контейнер PostgreSQL с правилом PostgreSQLContainer . Затем мы можем использовать вспомогательные методы. Это getJdbcUrl, getUsername, getPassword для подключения к базе данных:

@Rule
public PostgreSQLContainer postgresContainer = new PostgreSQLContainer();

@Test
public void whenSelectQueryExecuted_thenResulstsReturned()
throws Exception {
String jdbcUrl = postgresContainer.getJdbcUrl();
String username = postgresContainer.getUsername();
String password = postgresContainer.getPassword();
Connection conn = DriverManager
.getConnection(jdbcUrl, username, password);
ResultSet resultSet =
conn.createStatement().executeQuery("SELECT 1");
resultSet.next();
int result = resultSet.getInt(1);

assertEquals(1, result);
}

Также возможно запустить PostgreSQL как универсальный контейнер. Но было бы сложнее настроить соединение.

4.2. Веб-драйверы

Другой полезный сценарий — запуск контейнеров с помощью веб-браузеров. Правило BrowserWebDriverContainer позволяет запускать Chrome и Firefox в контейнерах docker-selenium . Затем мы управляем ими с помощью RemoteWebDriver.

Это очень полезно для автоматизации UI/приемочных тестов для веб-приложений:

@Rule
public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer()
.withCapabilities(new ChromeOptions());
@Test
public void whenNavigatedToPage_thenHeadingIsInThePage() {
RemoteWebDriver driver = chrome.getWebDriver();
driver.get("http://example.com");
String heading = driver.findElement(By.xpath("/html/body/div/h1"))
.getText();

assertEquals("Example Domain", heading);
}

4.3. Докер Сочинять

Если для тестов требуются более сложные сервисы, мы можем указать их в файле docker-compose :

simpleWebServer:
image: alpine:3.2
command: ["/bin/sh", "-c", "while true; do echo 'HTTP/1.1 200 OK\n\nHello World!' | nc -l -p 80; done"]

Затем мы используем правило DockerComposeContainer . Это правило запускает и запускает службы, как определено в файле компоновки.

Мы используем методы getServiceHost и getServicePost для построения адреса подключения к сервису:

@ClassRule
public static DockerComposeContainer compose =
new DockerComposeContainer(
new File("src/test/resources/test-compose.yml"))
.withExposedService("simpleWebServer_1", 80);

@Test
public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
throws Exception {

String address = "http://" + compose.getServiceHost("simpleWebServer_1", 80) + ":" + compose.getServicePort("simpleWebServer_1", 80);
String response = simpleGetRequest(address);

assertEquals(response, "Hello World");
}

5. Вывод

Мы увидели, как можно использовать библиотеку TestContainers . Это упрощает разработку и запуск интеграционных тестов.

Мы использовали правило GenericContainer для контейнеров заданных образов докеров. Затем мы рассмотрели правила PostgreSQLContainer, BrowserWebDriverContainer и DockerComposeContainer . Они дают больше функциональности для конкретных случаев использования.

Наконец, примеры кода здесь можно найти на GitHub .