16.5 Perfect Integration Test Environments with Testcontainers
Testing with H2 in-memory DB can lead to false positives — tests pass but fail in production due to SQL syntax differences and constraint variations between H2 and real databases. Testcontainers spins up real Docker containers during tests, providing a perfectly identical DB environment.
1. Adding Dependencies
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:mysql'
2. @SpringBootTest + Testcontainers Integration Test
@SpringBootTest
@Testcontainers
class OrderIntegrationTest {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
registry.add("spring.datasource.username", mysql::getUsername);
registry.add("spring.datasource.password", mysql::getPassword);
}
@Autowired
private OrderService orderService;
@Autowired
private UserRepository userRepository;
@Test
@DisplayName("Order creation is persisted correctly to the real DB.")
@Transactional
void create_order_integration() {
User user = userRepository.save(new User("test", "test@example.com"));
Long orderId = orderService.createOrder(new CreateOrderRequest(user.getId(), 1L, 2));
assertThat(orderId).isNotNull().isPositive();
}
}
3. Testcontainers + Redis
@Container
static GenericContainer<?> redis = new GenericContainer<>("redis:7.0")
.withExposedPorts(6379);
@DynamicPropertySource
static void configureRedis(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", redis::getHost);
registry.add("spring.data.redis.port", () -> redis.getMappedPort(6379));
}