Testcontainers + DB Rider = конец проблем с данными в интеграционных тестах

Testcontainers + DB Rider = конец проблем с данными в интеграционных тестах

Если у тебя когда-нибудь “плавали” интеграционные тесты из-за грязной БД — ты знаешь эту боль. Testcontainers решает только половину проблемы: он поднимает чистый контейнер. Но что происходит внутри теста? Как гарантировать предсказуемые данные и стабильные проверки?

Ответ — DB Rider.

Testcontainers для тех, кто не знает, что это

Testcontainers — библиотека для интеграционных тестов, которая позволяет поднимать контейнеры Docker с различными образами (PostgreSQL, Kafka, Redis, MiniO, любые другие контейнеры) прямо в тестах, автоматически и изолированно. Вместо моков или «in-memory» мы можем работать с настоящими сервисами и инфраструктурой, развернутой посредством Docker или другой системой контейнеризации. Контейнеры стартуют перед тестами и автоматически удаляются после выполнения.

Почему именно эта комбинация

  • Testcontainer поднимает настоящие базы данных в контейнерах.
  • Database Rider обеспечивает простой и декларативный механизм инициализации данных перед тестом.
  • Database Rider дает возможность проверить снимок данных после выполнения тестов.
  • Вместе они дают полную изоляцию тестов и повторяемость результатов.
  • Нет необходимости настраивать дополнительные экземпляры СУБД для проведения интеграционных тестов.

Пример использования Testcontainer и Database Rider

Изменим предыдущий пример, чтобы вместо H2 использовать Testcontainer с PostgreSQL. Для этого нам нужно будет убрать зависимости с H2 и добавить зависимости для TestContainers:

dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'com.github.database-rider:rider-junit5:1.44.0' testImplementation 'com.github.database-rider:rider-spring:1.44.0' // Удаляем h2 // runtimeOnly 'com.h2database:h2' // добавляем необходимые зависимости // --- JDBC Driver --- runtimeOnly 'org.postgresql:postgresql:42.7.3' // --- Testcontainers --- testImplementation 'org.testcontainers:junit-jupiter' testImplementation 'org.testcontainers:postgresql' }

Теперь для того, чтобы правильно подхватывались настройки datasource, мы изменим файл свойств application-test.yml, удалим всё, связанное с h2, и добавим строку подключения к контейнеру с PostgreSQL:

spring: datasource: url: jdbc:tc:postgresql:16-alpine:///testdb?TC_INITSCRIPT=sql/init-schema.sql

Здесь init-schema.sql - файл скрипта для инициализации схемы БД, который будет выполняться при запуске контейнера:

CREATE TABLE IF NOT EXISTS users_info ( id SERIAL PRIMARY KEY, name TEXT NOT NULL );

Чтобы наш тест заработал с использованием TestContainers, нам нужно будет добавить аннотацию @Testcontainers в класс теста.

Еще пара важных моментов:

  • Необходимо добавить аннотацию @DBUnit(schema = "public", caseSensitiveTableNames = true) в класс теста, чтобы указать схему базы данных, которую мы хотим использовать для тестов и убрать чувствительность к регистру иначе Database Rider не увидит наши таблицы.
  • И вместо @DataJpaTest использовать @SpringBootTest - потому что при использовании @DataJpaTest каждый метод оборачивается в транзакцию, а проверка Database Rider происходит после того как транзакция отменяется, поэтому он не увидит данные, которые были добавлены в таблицу.

Теперь можно запустить тесты и убедиться, что все работает как надо:

./gradlew test

Полный код примера доступен по ссылке

Заключение

Комбинация Testcontainers и Database Rider предоставляет:

  • Окружение для тестирования, приближенное к продакшену.
  • Контроль и предсказуемость состояния БД.
  • Скорость создания и модификации интеграционных тестов.
  • Гибкость и поддержку сложных сценариев интеграционных тестов.

Используете у себя в проектах Testcontainers? Трудно ли поддерживать сложные сценарии?

Подпишись на мой канал в telegram

Начать дискуссию