Надоели моки репозиториев, которые не ловят реальные SQL-баги?
Настраиваем Testcontainers, запускаем настоящий PostgreSQL прямо в тестах, без внешних зависимостей.
⚙️ Шаг 1 — Зависимости (Maven)
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
> Для Gradle:
testImplementation "org.testcontainers:postgresql:1.19.7"🐳 Шаг 2 — Базовый тест
@SpringBootTest
@Testcontainers
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
UserRepository userRepository;
@Test
void shouldSaveAndFindUser() {
var user = new User("alice@example.com");
userRepository.save(user);
var found = userRepository.findByEmail("alice@example.com");
assertThat(found).isPresent();
}
}
Всё. Spring подхватит datasource-проперти через @DynamicPropertySource — Liquibase/Flyway накатятся сами.
⚡️ Шаг 3 — Ускоряем: переиспользуем контейнер
По умолчанию контейнер поднимается на каждый тест-класс.
Чтобы один инстанс жил на весь прогон — выносим в абстрактный класс:
@SpringBootTest
@Testcontainers
public abstract class BaseIntegrationTest {
@Container
static final PostgreSQLContainer<?> POSTGRES =
new PostgreSQLContainer<>("postgres:16-alpine")
.withReuse(true); // ← ключевая строка
@DynamicPropertySource
static void props(DynamicPropertyRegistry r) {
r.add("spring.datasource.url", POSTGRES::getJdbcUrl);
r.add("spring.datasource.username", POSTGRES::getUsername);
r.add("spring.datasource.password", POSTGRES::getPassword);
}
}
Наследуй в каждом интеграционном тесте — контейнер поднимется один раз.
🔍 Шаг 4 — Отладка: смотрим логи контейнера
POSTGRES.followOutput(new Slf4jLogConsumer(
LoggerFactory.getLogger("postgres-container")
));
Добавь до старта контейнера и все PostgreSQL логи пойдут в твой аппендер.
🔖 Сохраняй пост для следующего проекта.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#Enterprise