Как использовать Docker в тестировании
Тестировщики часто проводят тестирование в разных контурах различных версий приложений. Если вам нужно настроить окружение в контуре только по двум сервисам, то вопрос с docker не актуален. А если их 20? Появляется проблема с настройкой сред окружения. Тут приходит на помощь docker.
Что такое Docker
Docker – это ПО для автоматизации развертывания и управления приложениями в средах с поддержкой контейнеризации. Он помогает “упаковать” приложение со всем его окружением и зависимостями в образ, который достаточно просто разворачивается в контейнере в любой Linux системе.
Контейнер – это изолированный процесс, в который вы упаковываете свое приложение со всеми зависимостями.
Почему стоит использовать docker в тестах
-
Проблема запуска тестов в команде без установки стека тестовых технологий (solenoid, браузеры и др.)
-
Менее требовательный, чем виртуальная машина по ресурсам (масштабирование ограничивается только ресурсами памяти и процессора на узлах с docker)
-
Проблема с инсталляцией дополнительного софта в разных контурах т.к. имеются ограничения
-
Использование TestContainers решает многие проблемы. Например, запуск kafka в docker для работы приложения или интеграционных сервисов.
Главными преимуществами докера является его простота и дружелюбность в поведении в инструментах автоматизации. Таким образом, с помощью docker вы всегда сможете подтянуть нужные вам образа с программным обеспечением из docker hub либо собрать образ самостоятельно (проблема с заявками по инсталляции доп. ПО на ваш сервер уходит в прошлое). Например, вы можете использовать готовые docker image: kafka, PostgreSQL, Selenoid и др. Также можно отметить, что запуск тестов можно выполнить с любого удобного вам сервера вместо агента. Данная технология кроссплатформенная, т.е. запускается в разных ОС.
К минусам можно отнести повышенной порог входа для работы, а также особенности масштабирования.
Сборка docker image, запуск docker контейнера, запуск тестов в контейнере
Практическая часть будет продемонстрирована на ОС Win.
Обратите внимание, что на Win и Mac используется не настоящий Docker, а виртуальные машины Linux, в которых работает Docker.
-
Установите docker desktop
-
Создайте проект с пакетным менеджером maven в intellij idea
-
Вставьте следующие зависимости в pom файл
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.testit.rest.assured</groupId>
<artifactId>rest-assured</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>rest-assured-sample</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<junit.platform.version>1.7.0</junit.platform.version>
<junit.jupiter.version>5.7.0</junit.jupiter.version>
<restAssured.version>4.2.0</restAssured.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit.platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${restAssured.version}</version>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
<groupId>org.apache.maven.plugins</groupId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
</plugins>
</build>
</project>
4. Создайте структуру папок
├───src
│ └───test
│ ├───java
│ │ └───com
│ │ ├───apiTests
│ │ ├───model
│ └───resources
5. Создайте модели (DTO)
Создадим класс UserDTO с моделью для выполнения запросов GET, POST, PUT, DELETE в каталоге src/test/java/com/model:
public class UserDTO {
public String name;
public String job;
public UserDTO(String name, String job) {
this.name = name;
this.job = job;
}
}
6. Напишем простой автотест для сайта https://reqres.in для запросов GET, POST, PUT, DELETE с разным способом валидации Response:
- Тестирование запроса Get c проверкой status code = 200. Создайте класс GetRequestTest в каталоге src/test/java/com/apiTests с тестом getRequestCheckStatusCode:
public class GetRequestTest {
@Test
@DisplayName("Тестирование запроса Get c проверкой status code = 200")
public void getRequestCheckStatusCode() {
RestAssured.given()
.baseUri("https://reqres.in/")//---> Cтартовая URL
.relaxedHTTPSValidation()
.get("/api/users/2")//---> Endpoint для выполнения запроса GET
.then()
.statusCode(HttpStatus.SC_OK);//---> Проверка статус код
}
}
- Тестирование тестового запроса Put c обновлением данных Users по полю job. Создайте класс PostRequestTest в каталоге src/test/java/com/apiTests с тестом postRequestCheckStatusCode:
public class PostRequestTest {
@Test
@DisplayName("Тестирование тестового запроса Post с проверкой status code = 201")
public void postRequestCheckStatusCode() {
RestAssured.given()
.baseUri("https://reqres.in/")//---> Cтартовая URL
.relaxedHTTPSValidation()
.body(new UserDTO("morpheus", "leader"))//---> body для запроса с методом POST
.post("/api/users")//---> Endpoint для выполнения запроса GET
.then()
.statusCode(HttpStatus.SC_CREATED);//---> Проверка статус код
}
}
- Тестирование тестового запроса Put c обновлением данных Users по полю job. Создайте класс PutRequestTestкаталог src/test/java/com/apiTests с тестом putRequestCheckStatusCodeAndJsonBody:
public class PutRequestTest {
@Test
@DisplayName("Тестирование тестового запроса Put c обновлением данных Users по полю job")
public void putRequestCheckStatusCodeAndJsonBody() {
String id;
id = RestAssured.given()
.baseUri("https://reqres.in/")//---> Cтартовая URL
.relaxedHTTPSValidation()
.body(new UserDTO("morpheus", "leader"))//---> body для запроса с методом POST
.post("/api/users")//---> Endpoint для выполнения запроса GET
.then()
.statusCode(HttpStatus.SC_CREATED)//---> Проверка статус код
.assertThat()
.body("name", Matchers.is("morpheus"))//---> Проверка Body по key и value в json
.body("job", Matchers.is("leader"))//---> Проверка Body по key и value в json
.extract()
.response()
.body()
.path("id");//---> указания поля из Response Json для извлечения данных
RestAssured.given()
.baseUri("https://reqres.in/")//---> Cтартовая URL
.relaxedHTTPSValidation() .body(new UserDTO("morpheus", "test_it"))//---> body с обновленными данными для запроса с методом PUT
.post("/api/users" + id)//---> Endpoint для выполнения запроса GET
.then()
.statusCode(HttpStatus.SC_CREATED)//---> Проверка статус код
.assertThat()
.body("name", Matchers.is("morpheus"))//---> Проверка Body по key и value в json
.body("job", Matchers.is("test_it"));//---> Проверка Body по key и value в json
}
}
- Тестирование запроса Delete c удалением пользователя. Создайте класс DeleteRequestTest в каталоге src/test/java/com/apiTests с тестом deleteRequestCheckStatusCode:
public class DeleteRequestTest {
@Test
@DisplayName("Тестирование запроса Delete c удалением пользователя")
public void deleteRequestCheckStatusCode() {
int id;//---> Преобразование с int можно выполнять либо извлекать из Response значение id в виде строки
id = Integer.parseInt(RestAssured.given()
.baseUri("https://reqres.in/")//---> Cтартовая URL
.relaxedHTTPSValidation()
.body(new UserDTO("morpheus", "leader"))//---> body для запроса с методом POST
.post("/api/users")//---> Endpoint для выполнения запроса GET
.then()
.statusCode(HttpStatus.SC_CREATED)//---> Проверка статус код
.extract()
.response()
.body()
.path("id"));//---> указания поля из Response Json для извлечения данных
RestAssured.given()
.baseUri("https://reqres.in/")//---> Cтартовая URL
.relaxedHTTPSValidation()
.delete("/api/users/" + id)//---> Endpoint для выполнения запроса GET
.then()
.statusCode(HttpStatus.SC_NO_CONTENT);//---> Проверка статус код
}
}
7. Создадим DockerFile
DockerFile - это инструкция по созданию образа. Создайте в корне проекта файл Dockerfile c содержимым:
#FROM maven:3.6.3-jdk-11 — базовый образ с предустановленным mvn 3.6.3 и jdk11
FROM maven:3.6.3-jdk-11
#RUN mkdir -p /usr/src/app — создание каталога /usr/src/app в контейнере
RUN mkdir -p /usr/src/app
#WORKDIR /usr/src/app — задаёт рабочую директорию для следующей инструкции.
WORKDIR /usr/src/app
#ADD . /usr/src/app — копирование файлов в директорию. см. ADD or COPY: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
ADD . /usr/src/app
# RUN mvn clean test — выполняет команду и создаёт слой образа. Используется для установки в контейнер пакетов.
RUN or CMD: https://www.geeksforgeeks.org/difference-between-run-vs-cmd-vs-entrypoint-docker-commands/#:~:text=A%20CMD%20command%20is%20used,the%20last%20one%20gets%20executed.
Можно указать .sh для построение сложной логики запуска тестов, например, проверка на доступность тестируемого приложения
RUN mvn clean test
8. Выполним сборку docker image:
- Откройте терминал intellij idea в корне проекта
- Выполните команду: docker build -t test-docker:1.0 .
docker — запуск команды Docker
build — команда сборки образа Docker
-t — присвоить образу tag с названием 1.0
test-docker — имя образа
. [точка] — указывает, что DockerFile находится в текущей папке
Результатом выполнения должно быть
[+] Building 40.6s (10/10) FINISHED
- Проверьте Docker Desktop. В разделе images появился новый образ test-docker c tag 1.0 или введите в терминале команду: docker images
docker — запуск команды Docker
images — запуск команды вывода идентификаторов образов
9. Выполните запуск тестов следующим образом:
- docker run --name teststartdocker -p 4444:4444 test-docker:1.0
docker — запуск команды Docker
run — команда запуска образа Docker
--name run-test-docker — присваивание имя контейнера
-p 4444:4444 — порты на входе и выходе контейнера
test-docker:1.0 — имя образа и версия(tag) для запуска контейнера
- Результат выполнения:
[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
…
[INFO] BUILD SUCCESS
- Проверка запущенного контейнера docker ps -a
Примечание: можно использовать ключ -rm для удаление контейнера после его выполнения. Пример docker run --rm --name teststartdocker -p 4444:4444 test-docker:1.0
10. Остановка docker контейнера выполняется следующим образом docker stop [NAME или IMAGE+TAG]
Пример:
docker stop test-docker:1.0
docker stop 427835437fb9
11. Далее вы можете удалить контейнер или образ.
- Удаление контейнера docker rm [NAME или IMAGE]
Пример: docker rm 427835437fb9
docker rm teststartdocker
- Удаление образа docker rmi [REPOSITORY:TAG или IMAGE ID+TAG] (i от image)
Пример: docker rmi test-docker:1.0
docker rmi 94847b9b00cd
Примечание: При изменении состава контейнера, вы не изменяете образ.
Основные команды docker:
-
docker ps — просмотр списка запущенных контейнеров
-
docker pull — загрузка образа.
Как правило, образы создаются на основе базового — из Docker Hub, где есть множество уже готовых образов и которые ты можешь использовать, а не тратить время на создание собственного. Для загрузки образа используется команда docker pull.
-
docker build — собирает образ.
Данная команда собирает образ Docker из файла докера (dockerfile) и контекста сборки. Контекст сборки — это набор файлов, расположенных по определенному пути.
-
docker logs — смотрим логи
Позволяет просмотреть логи указанного контейнера. Можно использовать флаг -follow, чтобы следить за логами работающего контейнера, например, docker logs -follow my.
-
docker run — запускаем контейнер на основе указанного образа.
-
docker stop — останавливает контейнер.
Используется для «мягкой» остановки контейнера. Пример: docker stop test-docker:1.0. Можно остановить не конкретный контейнер, а все запущенные — docker stop $(docker ps -a -q).
-
docker kill — «убивает» контейнер.
Не пытается аккуратно завершить процесс, подобна системной команде kill. Как и в предыдущем случае, можно «убить» все контейнеры: docker kill $(ps -a -q).
-
docker rm — удаляет контейнер.
-
docker rmi — удаляет образ.
-
docker volume ls — список томов. Данная команда показывает список томов, которые являются основным механизмом для хранения данных, генерируемых контейнерами Docker.
Причины и цели для использования docker в тестировании могут быть разными, но современные реалии развития этой технологии показывают, что контейнеризация будет развивается и поддерживаться, т.е. пройти мимо нее не получится. Запуск ваших тестов (модульных, интеграционных или E2E) в контейнерах облегчает поддержку громоздких тестовых сред и помогает оптимизировать конвейеры непрерывной интеграции. Контейнеризация тестов может сэкономить вашей организации время на отладку проблем, вызванных несоответствием версий программного обеспечения.
В следующей статье познакомимся с Docker compose, создадим docker image с UI автотестами с запуском с помощью Maven.
Автор: Андрей Терешин