Testcontainers, DB integration
On this page
Why Integration Tests Exist
Unit tests validate logic. Integration tests validate reality. Reality includes: - database constraints - transaction behavior - migrations - serialization - connection pooling settings - query performance shape If you never test reality, production becomes your integration environment.Incident Scenario: Migration Broke Writes, Unit Tests Stayed Green
A migration added a column without default. The app wrote null and failed at runtime. Unit tests mocked repository and passed. Integration tests would have failed immediately.Testcontainers: Production-Like Dependencies in CI
Testcontainers runs real dependencies in containers during tests: - PostgreSQL/MySQL - Redis - Kafka - LocalStack This allows deterministic, repeatable integration testing in CI.Anti-Pattern: Using Shared Dev Database for Tests
Shared databases cause: - nondeterministic failures - data leaks across tests - environment coupling - painful cleanup Integration tests must own their environment.Spring Boot + Testcontainers Example
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@SpringBootTest
@Testcontainers
class UserRepositoryIT {
@Container
static PostgreSQLContainer> db = new PostgreSQLContainer<>("postgres:16")
.withDatabaseName("test")
.withUsername("test")
.withPassword("test");
@Test
void shouldPersistAndLoadUser() {
// real DB verification happens through repository
}
}
In real projects, wire container properties into Spring datasource configuration.