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