JavaRush /Курсы /Модуль 5. Spring /Лекция 140: Использование Testcontainers для тестирования...

Лекция 140: Использование Testcontainers для тестирования с реальной базой данных

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

Тестировать с использованием реальной базы данных жизненно необходимо в сложных приложениях. Представьте, что вы разрабатываете приложение, а на продакшене используете PostgreSQL. Вы пишете тесты, используя H2, и всё отлично работает… до первой встречи с реальным миром, где схему данных ожидает непредвиденный крах. Именно здесь на помощь приходит Testcontainers.

Testcontainers — это библиотека Java, которая позволяет удобно запускать изолированные Docker-контейнеры для использования в тестах. Она дает возможность поднять реальную базу данных (PostgreSQL, MySQL, MongoDB и другие) в контейнере, протестировать приложение и автоматически уничтожить контейнер после завершения теста. Никаких "бонусов" вроде остатков тестового мусора в вашей локальной машине.


Преимущества Testcontainers

  • Реальное поведение: использование той же базы данных, что и на продакшене, минимизирует вероятность неожиданностей.
  • Одно и то же окружение везде: контейнеры гарантируют одинаковое окружение для тестов на локальной машине, CI/CD и даже вашем холодильнике, если он поддерживает Docker.
  • Чистота: после теста контейнер уничтожается, оставляя вашу систему нетронутой.
  • Поддержка многих баз данных: PostgreSQL, MySQL, MariaDB, MongoDB, Cassandra и даже Kafka.

Установка Testcontainers

Зависимости Maven/Gradle

Для начала добавим Testcontainers в проект. Используем PostgreSQL в качестве примера базы данных.

Maven:


<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

Gradle:


testImplementation("org.testcontainers:postgresql:1.19.0")
Важно:
Для работы Testcontainers необходимо, чтобы Docker был установлен и запущен на вашей машине.

Простая настройка Testcontainers с PostgreSQL

Начнем с создания базового теста, который использует контейнер PostgreSQL.

Шаг 1: Подготовка тестового контейнера

Создадим класс с тестами, где мы будем использовать контейнер. Testcontainers позволяет настраивать контейнеры для PostgreSQL следующим образом:


import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;

public class PostgresContainerTest {

    @Test
    void testPostgresContainer() {
        try (PostgreSQLContainer
    postgres = new PostgreSQLContainer<>("postgres:15")) {
            // Запускаем контейнер
            postgres.start();

            // Выводим данные контейнера для проверки
            System.out.println("Postgres URL: " + postgres.getJdbcUrl());
            System.out.println("Username: " + postgres.getUsername());
            System.out.println("Password: " + postgres.getPassword());

            // Здесь можно подключиться к базе через JDBC и выполнить тестовые действия
        }
    }
}

Этот тест запускает контейнер с Postgres версии 15. После вызова start() контейнер поднимается, и Testcontainers автоматически находит доступный порт для базы. После завершения блока try контейнер останавливается.

Шаг 2: Интеграция с Spring Boot

Сделаем так, чтобы Spring Boot использовал этот контейнер в тестах. Настройка делается через @DynamicPropertySource.


import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@SpringBootTest
@Testcontainers
class DatabaseIntegrationTest {

    @Container
    private static final PostgreSQLContainer
    POSTGRES = new PostgreSQLContainer<>("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void overrideProperties(org.springframework.test.context.DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
        registry.add("spring.datasource.username", POSTGRES::getUsername);
        registry.add("spring.datasource.password", POSTGRES::getPassword);
    }

    @Test
    void contextLoads() {
        // Ваш тест здесь
    }
}

Что здесь происходит?

  1. Аннотация @Testcontainers: указывает, что мы используем Testcontainers.
  2. Аннотация @Container: автоматически управляет жизненным циклом контейнера (запуск/остановка).
  3. @DynamicPropertySource: переопределяет свойства spring.datasource, чтобы Spring использовал параметры контейнера.

Написание теста для репозитория

Добавим тест для репозитория, в котором проверим сохранение сущности в базу.

Сущность 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;

    // Геттеры и сеттеры
}

Репозиторий:


import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

Тест:


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.junit.jupiter.Testcontainers;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@Testcontainers
class UserRepositoryTest {

    @Container
    private static final PostgreSQLContainer
    POSTGRES = new PostgreSQLContainer<>("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void overrideProperties(org.springframework.test.context.DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
        registry.add("spring.datasource.username", POSTGRES::getUsername);
        registry.add("spring.datasource.password", POSTGRES::getPassword);
    }

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveUser() {
        User user = new User();
        user.setName("John Doe");

        User savedUser = userRepository.save(user);
        assertThat(savedUser.getId()).isNotNull();
        assertThat(savedUser.getName()).isEqualTo("John Doe");
    }
}

Этот тест запускает контейнер базы данных, подключает его к Spring Boot и позволяет протестировать сохранение сущности без необходимости вручную поднимать Postgres.


Особенности и ошибки

  • Docker должен быть установлен и запущен: если Docker не работает, ваши тесты просто откажутся запускаться.
  • Порт 5432 уже занят? Testcontainers автоматически использует произвольный порт для контейнера, поэтому конфликты исключены.
  • Долгая инициализация? первый запуск может занять больше времени из-за загрузки образа из Docker Hub.

Практическое применение

  1. Реальная среда тестирования: вы можете быть уверены, что ваше приложение будет работать так же в продакшене, как и в тестах.
  2. Автоматизация CI/CD: включив Testcontainers в пайплайны (например, GitHub Actions или GitLab CI), вы сделаете интеграционные тесты более надежными.
  3. Динамическое тестирование разных конфигураций: просто меняйте параметры контейнера (например, версию Postgres) и проверяйте, что всё работает корректно.

Testcontainers — это мощное средство для интеграционного тестирования с реальными базами данных, которое делает ваш тестовый процесс более надежным и приближенным к реальной эксплуатации. Учитесь использовать его, и не забудьте, что контейнеры после тестов всё же нужно выключать... Хорошо, что Testcontainers делает это автоматически! 😉

Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Артём Уровень 112
18 сентября 2025
Если будете эти тесты гонять, то postgres лучше какой-нить поновее используйте, проще всего latest, а то проблемы вылезут. И не забудьте spring.jpa.hibernate.ddl-auto=create в application.properties добавить.