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

Лекция 269: Практика: настройка Testcontainers для тестирования с базами данных

Модуль 5. Spring
27 уровень , 8 лекция
Открыта

На прошлых лекциях мы погрузились в тестирование микросервисных приложений. Мы разобрались с различными подходами и видами тестирования, включая юнит-тестирование с использованием 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 может облегчить жизнь разработчику и тестировщику. Тестирование с использованием реальных баз данных теперь стало доступным и простым, а тесты стали более надежными. Этот инструмент особенно полезен на интервью, где важно показать уверенность в тестировании микросервисов. В реальных проектах он помогает избежать ошибок при взаимодействии с продакшен-системами.

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

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ