Когда речь идёт о микросервисах, базы данных играют важнейшую роль. Ваш код может выглядеть идеально, но если SQL-запросы не работают так, как надо, или структура базы данных отличается от ожиданий, результат может быть плачевным. А теперь представьте, что у вас в системе 10 микросервисов. Ошибки на этом уровне могут в буквальном смысле повергнуть систему в хаос.
Интеграционное тестирование с базой данных поможет:
- Проверить, что SQL-запросы работают корректно.
- Убедиться, что аннотации JPA и сущности настроены правильно.
- Выполнить тесты миграций баз данных (например, Flyway или Liquibase).
- Убедиться, что транзакции обрабатываются корректно.
Однако поднимать реальную базу данных для каждого тестирования – сложно и долго. Тут на сцену выходит Testcontainers.
Что такое Testcontainers?
Testcontainers — это Java-библиотека для написания тестов, использующих Docker-контейнеры. Она позволяет поднимать и уничтожать временные контейнеры для баз данных, брокеров сообщений, веб-серверов и других сервисов, прямо во время выполнения тестов. Красота заключается в том, что как только тесты запускаются, контейнеры поднимаются по необходимости, а после завершения тестирования автоматически уничтожаются.
Пример из жизни: представьте, что вы берёте машину в каршеринг. Вы используете её только тогда, когда это нужно, и сдаёте обратно. Testcontainers работает по аналогичной схеме с Docker-контейнерами для тестов.
Как работает Testcontainers?
- Вы указываете, какой контейнер нужно запустить (например, PostgreSQL).
- Контейнер поднимается перед тестом.
- Код теста подключается к контейнеру и тестирует взаимодействие.
- После завершения теста контейнер автоматически удаляется с вашей машины.
Основное преимущество: Нет зависимости от реальных баз данных и внешних сервисов. Всё изолировано.
Установка Testcontainers
Для начала добавим зависимости в pom.xml (если вы используете Maven):
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
Если вы используете Gradle, то добавляем:
testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
testImplementation 'org.testcontainers:postgresql:1.19.0'
Кроме того, убедитесь, что у вас установлен Docker и он работает. Если вы используете Docker Desktop, то можно уже радоваться.
Ваш первый тест с Testcontainers
Начнём с создания самого простого теста. Мы поднимем контейнер PostgreSQL и проверим, что можем к нему подключиться:
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class PostgresTest {
@Test
public void testPostgresContainer() {
// Создаём PostgreSQL контейнер
try (PostgreSQLContainer
postgres = new PostgreSQLContainer<>("postgres:15.1")) {
// Запускаем контейнер
postgres.start();
// Проверяем, что контейнер запущен, а URL подключения не пустой
assertNotNull(postgres.getJdbcUrl());
System.out.println("PostgreSQL запущен на " + postgres.getJdbcUrl());
}
}
}
Если вы запустите этот тест, Docker поднимет контейнер PostgreSQL, выполнит тест и уничтожит контейнер. Посмотрите в консоль: там должна появиться информация о запущенной базе данных.
Интеграция с Spring Data JPA
Теперь подключим Testcontainers к реальному приложению. Мы будем использовать Spring Boot и JPA.
1. Конфигурация Spring Boot
В файле application.properties укажите "заглушечные" параметры для базы данных (они будут переопределены Testcontainers на этапе тестирования):
spring.datasource.url=jdbc:tc:postgresql:15.1://localhost/testdb
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.hibernate.ddl-auto=update
Обратите внимание на формат jdbc:tc:postgresql:15.1://localhost/testdb. Testcontainers автоматически подтянет контейнер PostgreSQL указанной версии!
2. Создание сущности
Добавим сущность User:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
// геттеры и сеттеры
}
3. Создание репозитория
Создаём простой интерфейс репозитория:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
4. Тестирование репозитория с Testcontainers
Добавим тесты для проверки операций CRUD. Testcontainers сам создаст контейнер PostgreSQL при запуске тестов:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void testSaveUser() {
// Сохраняем нового пользователя
User user = new User();
user.setName("John Doe");
User savedUser = userRepository.save(user);
// Проверяем, что пользователь сохранился
assertEquals("John Doe", savedUser.getName());
}
}
Когда вы запустите этот тест, Testcontainers поднимет контейнер PostgreSQL, создаст базу данных testdb, а затем выполнит SQL-операции. По завершении контейнер будет автоматически уничтожен.
Преимущества Testcontainers
- Изоляция окружения: каждая тестовая база данных запускается в отдельном контейнере.
- Минимальная настройка: не нужно руками поднимать базы данных или удалять их после тестов.
- Реалистичность: вы тестируете код на реальной базе данных, а не на эмуляции.
- Поддержка множества баз данных и сервисов: например, MySQL, MongoDB, Redis, Kafka.
Типичные ошибки и особенности использования
- Ошибка "Docker not found". Убедитесь, что Docker запущен, и ваша среда разработки имеет к нему доступ.
- Медленные тесты. Поднятие контейнера занимает время. Используйте общие контейнеры для группы тестов, чтобы ускорить процесс.
- Утечки ресурсов. Не забывайте закрывать контейнеры после тестов, если вы запускаете их вручную (не автоуправляемые Testcontainers).
Где это пригодится?
- Разработка микросервисов: поднимаете реальную базу или брокер сообщений (Kafka, RabbitMQ) для тестов.
- Проверка миграций: убедитесь, что ваши Flyway-миграции работают на всех целевых базах.
- Собеседования: на вопрос "Как вы тестируете базы данных в своих проектах?" ваш ответ "Testcontainers" вызовет интерес и дополнительный плюс.
Теперь вы знаете, как Testcontainers позволяет вам поднимать реальное окружение для тестирования прямо из кода. Запустите тесты и почувствуйте магию изоляции Docker! В следующих лекциях мы продолжим исследовать микросервисы и другие инструменты тестирования.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ