Эксперименты с Тестконтейнерами
Описание
Основная цель - разобраться с возможными вариантами поднятия тестового окружения с помощью testcontainers или docker-compose.
Статья на Habr: https://habr.com/ru/articles/986870/
Дополнительные цели:
- testcontainers: разобраться, как настраивать контейнеры на работу без перезапуска при ребилде приложения
- devtools: разобраться, как пользоваться
- создать примеры тестов, которые можно использовать для интеграционного тестирования
Приложение:
- Spring Web
- Spring Data JPA + для генерации идентификаторов воспользуемся TSID от Vlad Mihalcea https://vladmihalcea.com/uuid-database-primarykey
- Postgresql
- Кафку будем конфигурировать вручную, без использования Spring Kafka, для имитации необходимости подключаться к разным источникам
- Для обращений по rest к сторонним сервисам создадим Spring-клиента через интерфейс HttpExchange
- Для конвертации dto используем mapstruct
- Для миграций используем liquibase
Что будем тестировать:
- обработка входящего rest-запроса и сохранение результатов в БД
- получение данных из БД по rest-запросу
- отправка сообщения в Кафку
- получение сообщения из Кафки
- получение ответа от стороннего сервиса по rest
Что надо для интеграционного тестирования (реализовано в TestAppicationConfiguration):
- Поднять постгрес
- Поднять кафку. Если источников будет больше одного, то на каждый поднимем отдельный контейнер.
- Поднять wiremock rest-сервер для имитации внешнего rest-сервиса. Если внешних сервисов будет больше, то на каждый поднимем отдельный контейнер.
- Установка пропертей приложения на основе поднятых контейнеров: порты, url и так далее
- Заполнение базы тестовыми данными
- Создание базовых rest-моков, которые будут использоваться при старте тестового приложения
- При необходимости создание топиков в Кафке
- Для запросов к приложению используем RestAssured, чтобы имитировать полностью внешний запрос. При переходе на Spring Boot 4 можно будет выпилить и использовать штатные средства.
- Для сравнения ожидаемых ответов приложения с актуальными будем сравнивать JSON, потому что так проще сохранять эталонные ответы
Что хотелось бы показать еще, но пока не реализовано: [ ] Добавление и зачистка тестовых данных в БД для конкретных тестов. Не используем @Transactional. [ ] Стаб для LocalDateTime [ ] Проверка содержимого логов в тестовых целях [ ] Подключение JaCoCo/SonarQube
Запуск
Gradle
Версии зависимостей gradle см. в gradle/libs.versions.toml
Сборка
./gradlew clean build --info
Запуск тестовой версии приложения с поднятием тестового окружения
./gradlew bootTestRun
Повторная сборка приложения при изменениях классов - devtools автоматически выполнит рестарт приложения
./gradlew build -x test
Maven
Сборка
./mvnw clean verify
Запуск тестовой версии приложения с поднятием тестового окружения
./mvnw spring-boot:test-run
Повторная сборка приложения при изменениях классов - devtools автоматически выполнит рестарт приложения
./mvnw compile
Эндпоинты для проверки
curl -X 'POST' 'http://localhost:8095/order/create' -H "Content-Type: application/json" -d '{"customerId": 123456,"items": [{"productId": 888,"quantity": 20, "amount":100.00}]}'
curl 'http://localhost:8095/order/list?customerId=654321'
curl 'http://localhost:8095/order/sum?customerId=654321'
curl -X 'POST' 'http://localhost:8095/payment/pay' -H "Content-Type: application/json" -H "X-Customer-Id: 123456" -d '{"amount":100.00}'
Матчасть
- Testcontainers Support in Spring
- Testcontainers in Spring
- The best way to use Testcontainers with Spring Boot
- Tremendous Simplification of SpringBoot Development with TestContainers
- Developer Tools
- Wiremock
Варианты реализации
Spring way - через бины
- @TestConfiguration
- @Bean
- @Import + @Autowired для контейнеров
- @RestartScope чтобы не гасить контейнеры при ребилде
Через аннотации из Testcontainers -> Поднимаем контейнеры вручную
Вместо этой одной должно было быть две реализации - одна с использованием аннотаций @Testcontainers, а вторая - с ручным поднятием контейнеров. Но потом я перечитал статью The best way to use Testcontainers with Spring Boot (ссылка выше) и решил ограничиться одной реализацией - с ручным подъемом контейнеров. Тем более что эту реализацию я довольно долго использовал ранее.
docker-compose
Некоторой альтернативой использования Testcontainers для обеспечения приложения тестовым окружением может быть использование docker-compose. То есть просто поднимаем всю инфраструктуру приложения в Докере рядом. Это не позволит использовать тестовую инфраструктуру в тестах. Но просто поднять окружение, запустить приложение и подебажить его - вполне.
docker-compose см. в папке docker. Подробное описание, как и что делать - в файле ./docker/README.md
Дополнительно:
Обновление зависимостей
Gradle
./gradlew dependencyUpdates -Drevision=release -DoutputFormatter=html
Отчет ищи тут: ./build/dependencyUpdates/report.html
Maven
./mvnw versions:display-dependency-updates
./mvnw versions:display-plugin-updates