JavaRush /Курси /Модуль 5. Spring /Лекція 269: Практика: налаштування Testcontainers для тес...

Лекція 269: Практика: налаштування Testcontainers для тестування з базами даних

Модуль 5. Spring
Рівень 21 , Лекція 8
Відкрита

На попередніх лекціях ми занурилися в тестування мікросервісних додатків. Ми розібралися з різними підходами та видами тестування, включно з unit-тестуванням з використанням JUnit і Mockito, інтеграційним тестуванням REST API за допомогою MockMvc, а також розглянули контрактне тестування з Pact. Ми почали підбирати інструменти, які допомагають забезпечити надійність взаємодії між компонентами і сервісами. Тепер настав момент розглянути, як нам тестувати взаємодію з базами даних, використовуючи інструмент тестування на основі Docker — Testcontainers.

Раніше ми з'ясували, що тестування з базами може бути складним через необхідність ізоляції даних і створення реалістичного оточення. Сьогодні ми це виправимо!

Навіщо потрібні Testcontainers?

Уявіть, що ви тестуєте взаємодію з базою даних, і раптом хтось випадково додає невалідні дані в тестову базу. Ваші тести падають, ви починаєте розбиратися, і суцільний хаос. Або інша ситуація: ви тестуєте додаток на PostgreSQL, але на проді у вас буде MySQL. Що робити? Оточення для тестів повинно бути:

  1. Ізольованим — щоб кожне виконання тесту не залежало від зовнішніх факторів.
  2. Реалістичним — щоб тести запускалися з тими ж налаштуваннями, що і в продакшні.
  3. Легким у налаштуванні — щоб не вимагати складної інфраструктури.

Testcontainers дозволяє запускати контейнері Docker прямо під час виконання тестів, забезпечуючи ізольоване і реалістичне оточення для тестування. Це означає, що ви можете брати ті ж бази даних, брокери повідомлень і інші залежності, що використовуються в реальному світі. Готові? Поїхали!


Підготовка до роботи з Testcontainers

  1. Додамо залежності

    На початку потрібно підключити 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.

  2. Переконайтеся, що встановлено Docker Testcontainers працює через Docker, тож переконайтеся, що він встановлений і запущений на вашій машині.
  3. Мінімальна конфігурація 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 контекст.


Основні помилки та їх усунення

  1. Docker не запущений Якщо Docker не працює, Testcontainers просто не зможе піднімати контейнери. Переконайтеся, що Docker запускається коректно.
  2. Проблеми з мережею Іноді контейнери не можуть взаємодіяти з додатком через налаштування мережі. Перевірте, чи не працюєте ви в ізольованому режимі Docker (наприклад, при відсутності інтернету).
  3. Не вистачає ресурсів Бази даних можуть бути "важкими" для локальної машини. Переконайтеся, що у вашої машини достатньо пам'яті і CPU для запуску.

Практичне застосування

Ви щойно побачили, як Testcontainers може полегшити життя розробнику і тестувальнику. Тестування з використанням реальних баз даних тепер стало доступним і простим, а тести стали більш надійними. Цей інструмент особливо корисний на співбесідах, де важливо показати впевненість у тестуванні мікросервісів. У реальних проєктах він допомагає уникнути помилок при взаємодії з production-системами.

Для подальшого вивчення рекомендація: зверніть увагу на документацію Testcontainers.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ