Как правильно тестировать Spring Boot приложение с Testcontainers и несколькими профилями БД?
Проблема:
Я разрабатываю Spring Boot приложение с поддержкой разных СУБД (PostgreSQL, H2 для тестов). Хочу настроить интеграционные тесты с Testcontainers, но столкнулся с проблемами:
- При запуске тестов с
@DataJpaTestконтейнер PostgreSQL не инициализируется - Нужно поддерживать два режима тестирования:
- Быстрые тесты на H2 (для изолированных unit-тестов)
- Полноценные тесты на PostgreSQL в контейнере (для интеграционных тестов)
Что я пробовал:
Добавлял @Testcontainers и @Container (как в документации)
Менял @DataJpaTest на @SpringBootTest - тогда работает, но тесты выполняются дольше.
Пробовал разные комбинации @AutoConfigureTestDatabase
Текущая реализация:
// Тестовый класс
@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
// ... остальные параметры
}
@Test
void shouldSaveUser() {
// ... тест падает с NPE
}
}
Ошибка:
java.lang.NullPointerException: Cannot invoke "org.testcontainers.containers.PostgreSQLContainer.getJdbcUrl()" because "com.example.UserRepositoryTest.postgres" is null
Ответы (2 шт):
Если вы собираетесь тестировать контейнер, то его надо запускать при выполнении тестов. Статическая инициализация поля ни к чему не приводит, так как переменная остаётся не инициализированной, что приводит к NPE.
Попробуйте добавить инициализацию контейнера в тестируемый метод.
@Test
void shouldSaveUser() {
try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE)) {
postgres.start();
String jdbcUrl = postgres.getJdbcUrl();
assertNotNull(jdbcUrl);
}
}
Предлагаю перейти на @SpringBootTest для интеграционных тестов. типо так:
@SpringBootTest
@Testcontainers
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UserRepositoryIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@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);
registry.add("spring.datasource.driver-class-name", postgres::getDriverClassName);
}
@Autowired
private UserRepository userRepository;
@Test
void shouldSaveUser() {
var user = new User("vasya");
userRepository.save(user);
assertNotNull(user.getId());
}
}