На прошлых лекциях мы погрузились в тестирование микросервисных приложений. Мы разобрались с различными подходами и видами тестирования, включая юнит-тестирование с использованием JUnit и Mockito, интеграционное тестирование REST API с помощью MockMvc, а также рассмотрели контрактное тестирование с Pact. Мы начали подбирать инструменты, которые помогают обеспечить надежность взаимодействия между компонентами и сервисами. Теперь настал момент рассмотреть, как нам тестировать взаимодействие с базами данных, используя инструмент тестирования на основе Docker — Testcontainers.
Зачем нужны Testcontainers?
Представьте, что вы тестируете взаимодействие с базой данных, и вдруг кто-то случайно добавляет невалидные данные в тестовую базу. Ваши тесты падают, вы начинаете разбираться, и сплошной хаос. Или другая ситуация: вы тестируете приложение на PostgreSQL, но на продакшене у вас будет MySQL. Что делать? Окружение для тестов должно быть:
- Изолированным — чтобы каждое выполнение теста не зависело от внешних факторов.
- Реалистичным — чтобы тесты запускались с теми же настройками, что и в продакшне.
- Простым в настройке — чтобы не требовать сложной инфраструктуры.
Testcontainers позволяет запускать контейнеры Docker прямо во время выполнения тестов, обеспечивая изолированное и реалистичное окружение для тестирования. Это значит, что вы можете брать те же базы данных, брокеры сообщений и прочие зависимости, что используются в реальном мире. Готовы? Поехали!
Подготовка к работе с Testcontainers
- Добавим зависимости
Для начала нужно подключить Testcontainers к вашему проекту. Если вы используете PostgreSQL (любимый выбор тестировщиков), ваши зависимости в
pom.xmlбудут выглядеть так:<dependencies> <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> </dependencies>Если вы используете Gradle:
testImplementation 'org.testcontainers:junit-jupiter:1.19.0' testImplementation 'org.testcontainers:postgresql:1.19.0'Testcontainers поддерживает множество других баз данных, таких как MySQL, MongoDB, Oracle, и даже Kafka и RabbitMQ. Полный список можно найти в официальной документации Testcontainers.
- Убедитесь, что установлен Docker Testcontainers работает через Docker, так что убедитесь, что он установлен и запущен на вашей машине.
- Минимальная конфигурация Docker Вы должны быть уверены, что Docker может запускать контейнеры с базами данных. Простейшая проверка — выполнить команду
docker runнапрямую для PostgreSQL:docker run --name postgres-test -e POSTGRES_PASSWORD=test -d postgres:latestЕсли контейнер запускается без ошибок, вы готовы двигаться дальше!
Настройка теста с использованием Testcontainers
1. Запуск PostgreSQL контейнера
Начнем с создания самого простого контейнера PostgreSQL для тестирования. Вот пример:
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class PostgresContainerTest {
@Test
void testPostgresContainer() {
// Создаем контейнер PostgreSQL
try (PostgreSQLContainer
postgresContainer = new PostgreSQLContainer<>("postgres:latest")) {
postgresContainer.start();
// Проверяем, что база поднялась
String jdbcUrl = postgresContainer.getJdbcUrl();
String username = postgresContainer.getUsername();
String password = postgresContainer.getPassword();
System.out.println("JDBC URL: " + jdbcUrl);
System.out.println("User: " + username);
System.out.println("Password: " + password);
assertEquals("test", username);
}
}
}
Этот тест запускает контейнер PostgreSQL, получает URL для подключения к базе и демонстрирует, как вы можете его использовать. Конечно, практическая польза пока невелика, но это только начало.
2. Интеграция с Spring Data JPA
Теперь мы свяжем Testcontainers с вашим Spring Boot приложением. Пусть ваше приложение использует JPA, а в тестах мы будем поднимать контейнер с PostgreSQL.
Конфигурация application.properties для тестов
Создайте файл application-test.properties в папке src/test/resources:
spring.datasource.url=jdbc:tc:postgresql:latest:///testdb
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
jdbc:tc:postgresql:latest:///testdb — это небольшая магия Testcontainers. Она автоматически поднимет PostgreSQL контейнер и привяжет его данные.
Пример теста с использованием JPA
Допустим, у вас есть сущность User и репозиторий UserRepository:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Конструкторы, геттеры и сеттеры
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
Создадим интеграционный тест для репозитория:
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
void testSaveUser() {
// Создаем нового пользователя
User user = new User();
user.setName("Test User");
// Сохраняем его в базе
User savedUser = userRepository.save(user);
// Проверяем результат
assertNotNull(savedUser.getId());
assertEquals("Test User", savedUser.getName());
}
}
Теперь, запуская тест, Spring Boot автоматически поднимет контейнер PostgreSQL через Testcontainers.
3. Ручной контроль контейнера
В сложных сценариях вы можете захотеть управлять контейнером вручную. Например:
@Testcontainers
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class ManualPostgresContainerTest {
@Container
private static PostgreSQLContainer
postgresContainer = new PostgreSQLContainer<>("postgres:latest");
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgresContainer::getUsername);
registry.add("spring.datasource.password", postgresContainer::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void testSaveUserManualContainer() {
User user = new User();
user.setName("Another User");
User savedUser = userRepository.save(user);
assertNotNull(savedUser.getId());
assertEquals("Another User", savedUser.getName());
}
}
Здесь @Container управляет жизненным циклом контейнера, а @DynamicPropertySource позволяет подключить динамические параметры в Spring контекст.
Основные ошибки и их устранение
- Docker не запущен Если Docker не работает, Testcontainers просто не сможет поднимать контейнеры. Убедитесь, что Docker запускается корректно.
- Проблемы с сетью Иногда контейнеры не могут взаимодействовать с приложением из-за настройки сети. Проверьте, работаете ли вы в изолированном режиме Docker (например, при отсутствии интернета).
- Не хватает ресурсов Базы данных могут быть "тяжелыми" для локального компьютера. Убедитесь, что у вашей машины достаточно памяти и CPU для запуска.
Практическое применение
Вы только что увидели, как Testcontainers может облегчить жизнь разработчику и тестировщику. Тестирование с использованием реальных баз данных теперь стало доступным и простым, а тесты стали более надежными. Этот инструмент особенно полезен на интервью, где важно показать уверенность в тестировании микросервисов. В реальных проектах он помогает избежать ошибок при взаимодействии с продакшен-системами.
Для дальнейшего изучения рекомендация: обратите внимание на документацию Testcontainers.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ