middle
Что такое Testcontainers и зачем они нужны?
Testcontainers — библиотека, позволяющая запускать Docker-контейнеры прямо из тестов. Она решает проблему расхождения между тестовой (H2) и боевой (PostgreSQL, MySQL) базами данных, обеспечивая тестирование на реальной СУБД.
Зачем нужны
- H2 не поддерживает все возможности PostgreSQL (jsonb, оконные функции, специфичные типы)
- Тесты на реальной БД дают большую уверенность в коде
- Контейнеры автоматически создаются и уничтожаются
Подключение зависимостей
Пример
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
Пример с PostgreSQL
Пример кода
@SpringBootTest
@Testcontainers
class UserServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@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
private UserService userService;
@Test
void shouldSaveAndRetrieveUser() {
User user = userService.create(new UserDto("Иван", "ivan@example.com"));
assertNotNull(user.getId());
assertEquals("Иван", userService.findById(user.getId()).getName());
}
}
Базовый абстрактный класс для переиспользования контейнера
Пример кода
@Testcontainers
public abstract class AbstractPostgresTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@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);
}
}
@SpringBootTest
class OrderServiceTest extends AbstractPostgresTest {
// Переиспользует контейнер PostgreSQL
}
На собеседовании: интервьюер хочет услышать, зачем Testcontainers нужны вместо H2 и как подключить контейнер через
@DynamicPropertySource. Частая ошибка — не упомянуть абстрактный базовый класс для переиспользования контейнера между тестами.