На попередніх лекціях ми занурилися в тестування мікросервісних додатків. Ми розібралися з різними підходами та видами тестування, включно з unit-тестуванням з використанням 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 може полегшити життя розробнику і тестувальнику. Тестування з використанням реальних баз даних тепер стало доступним і простим, а тести стали більш надійними. Цей інструмент особливо корисний на співбесідах, де важливо показати впевненість у тестуванні мікросервісів. У реальних проєктах він допомагає уникнути помилок при взаємодії з production-системами.
Для подальшого вивчення рекомендація: зверніть увагу на документацію Testcontainers.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ