Это вторая часть серии статей о Spring Boot Testing. Фрагменты кода взяты из этого репозитория. Вы можете клонировать его и запускать тесты, чтобы увидеть, как он работает.

Главы

  1. Spring Boot Testing - Data and Services
  2. Spring Boot Testing - Testcontainers и Flyway

Итак, мы узнали, как тестировать уровень сервиса и уровень репозитория с помощью базы данных H2. У таких тестов есть некоторые преимущества. Например, они довольно быстрые и не требуют сложной настройки ни на локальной машине, ни в среде CI / CD. Однако H2 - это не та база данных, которая обычно запускается в производственной среде. Если мы используем некоторые особенности одной базы данных, специфичные для поставщика, H2 может нам не помочь. Итак, сегодня мы собираемся обсудить, как запускать тестовые примеры на реальном сервере БД.

Автоматическое создание схемы

Как мы можем запускать тесты с реальной базой данных? Что ж, мы могли бы создать новый экземпляр локально и настроить тестовую среду, отредактировав src/test/resources/application.yml. Это действительно работает, но из-за этого сложно воспроизвести сборку. Каждый разработчик, работающий над проектом, должен быть уверен, что у него есть две отдельные базы данных. Один для разработки, а другой для запуска тестов. Кроме того, это затрудняет сборку в среде CI / CD.

Итак, Testcontainers спешат на помощь! Это библиотека Java, которая запускает сервис в контейнере Docker, запускает тесты и, в конечном итоге, уничтожает контейнер. Вам не нужно ни о чем беспокоиться, фреймворк сделает всю работу. Просто убедитесь, что у вас установлен Docker, и тогда вы готовы к работе. Библиотека поддерживает десятки различных баз данных и модулей (PostgreSQL, MySQL, MongoDB, Kafka, Elasticsearch, Nginx, Toxiproxy и многие другие). Даже если вы не нашли тот, который вам нужен, вы можете использовать создание универсального контейнера.

Первый шаг - добавить необходимые зависимости.

testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
runtimeOnly 'org.postgresql:postgresql'

Затем нам нужно создать отдельный файл конфигурации в src/test/resources. Spring Boot умеет различать разные файлы конфигурации по профилям. Имя профиля должно быть добавлено в виде суффикса типа application-PROFILE_NAME.yml. Например, файл с именем application-test-containers.yml применяется, только когда активен профиль test-containers.

Вы заметили суффикс tc в строке JDBC-подключения? Это волшебство, которое дает объединение контейнеров JUnit 5 и Test. Дело в том, что вам вообще не нужны никакие программные конфигурации! Когда фреймворк видит, что url содержит суффикс tc, он выполняет все необходимые команды Docker внутри себя. Вы можете найти больше примеров здесь.

Мы устанавливаем свойство spring.jpa.hibernate.ddl-auto=create, чтобы схема базы данных создавалась автоматически в соответствии с определением классов сущностей. Интеграция с пролетным путем описана в следующем разделе.

Теперь давайте еще раз взглянем на метод PersonCreateService.createFamily и его тест H2.

Как запустить тест с экземпляром Testcontainers PostgreSQL? Все, что нам нужно сделать, это добавить две аннотации. @AutoConfigureTestDatabase хотя надо убрать.

  1. @ActiveProfiles("test-containers") - активирует профиль test-containers, чтобы Spring мог прочитать файл конфигурации, описанный ранее.
  2. @Testcontainers - указывает запускать экземпляр PostgreSQL в Docker автоматически

Видеть? Кусок пирога!

Репозиторий Тесты

А как насчет тестирования слоев репозитория? Давайте снова посмотрим пример H2.

Правила те же, но есть небольшая разница. @DataJpaTest аннотируется самим @AutoConfigureTestDatabase. Эта аннотация по умолчанию заменяет любой источник данных экземпляром H2. Итак, нам нужно изменить это поведение, добавив свойство replace=Replace.NONE.

По-прежнему все работает нормально.

Интеграция пролетного пути

Принцип эволюционного проектирования баз данных описан давно. В настоящее время использование инструментов, реализующих этот шаблон, является стандартной процедурой. Flyway и Liquibase - самые популярные в мире Java. Мы собираемся интегрировать Testcontainers с Flyway.

Во-первых, требуется зависимость Flyway.

implementation "org.flywaydb:flyway-core"

Во-вторых, необходимо отключить Flyway в application-test-containers.yml, потому что там будет отдельный файл конфигурации.

Затем мы собираемся создать application-test-containers-flyway.yml. Библиотека предоставляет множество автоконфигураций. Так что на самом деле нам ничего настраивать не нужно.

Пришло время добавить миграции SQL. Каталог по умолчанию - resources/db/migration.

Наконец, нам нужно заменить профиль test-containers на профиль test-containers-flyway.

Выполнение тестов CI / CD

Хотя цель Testcontainers состоит в том, чтобы упростить выполнение тестов, в сборке среды CI / CD есть некоторые предостережения. Некоторые поставщики прозрачно интегрируются с Testcontainers. Например, Travis-CI определяет необходимость автоматического запуска контейнера и выполняет эту работу внутренне. Но для некоторых других инструментов может потребоваться дополнительная настройка - например, Jenkins.

Докерская червоточина

Распространенный подход - упаковать всю сборку приложения в образ Docker во время выполнения CI. Testcontainers может обрабатывать его и запускать определенные контейнеры на хост-сервере Docker. В этом случае нам нужно привязать /var/run/docker.sock как том. Более того, каталог внутри контейнера должен быть таким же, как и тот, в котором контейнер был запущен.

Например, это гипотетическая команда для запуска тестов в среде CI с gradle.

docker run -it   \
           --rm  \
           -v $PWD:$PWD \
           -w $PWD  \
           -v /var/run/docker.sock:/var/run/docker.sock gradle:7.1-jdk8 \
           gradle test

Вы можете найти более подробную информацию на официальной странице руководства Testcontainers.

Заключение

Сегодня мы обсудили, как тестировать уровень данных и сервисов с помощью Testcontainers и Flyway. В следующий раз протестируем уровень API (контроллеры). Если у вас есть вопросы или предложения, оставляйте свои комментарии ниже. Спасибо за прочтение!