Testing JPA repositories with real databases using Testcontainers.
Testcontainers provides real database instances in Docker containers for integration testing. More reliable than H2 for production parity.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-postgresql</artifactId>
<scope>test</scope>
</dependency>@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryPostgresTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
@Autowired
private OrderRepository orderRepository;
@Autowired
private TestEntityManager entityManager;
}<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-mysql</artifactId>
<scope>test</scope>
</dependency>@Container
@ServiceConnection
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.4");@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class MultiDatabaseTest {
@Container
@ServiceConnection(name = "primary")
static PostgreSQLContainer<?> primaryDb = new PostgreSQLContainer<>("postgres:18");
@Container
@ServiceConnection(name = "analytics")
static PostgreSQLContainer<?> analyticsDb = new PostgreSQLContainer<>("postgres:18");
}Add to ~/.testcontainers.properties:
testcontainers.reuse.enable=trueThen enable reuse in code:
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.withReuse(true);@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.withInitScript("schema.sql");@SpringBootTest
@Testcontainers
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MigrationTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
@Autowired
private Flyway flyway;
@Test
void shouldApplyMigrations() {
flyway.migrate();
// Test code
}
}@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.withDatabaseName("testdb")
.withUsername("testuser")
.withPassword("testpass")
.withInitScript("init-schema.sql");@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.waitingFor(Wait.forLogMessage(".*database system is ready.*", 1));@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
@Autowired
private OrderRepository orderRepository;
@Autowired
private TestEntityManager entityManager;
@Test
void shouldFindOrdersByStatus() {
// Given
entityManager.persist(new Order("PENDING"));
entityManager.persist(new Order("COMPLETED"));
entityManager.flush();
// When
List<Order> pending = orderRepository.findByStatus("PENDING");
// Then
assertThat(pending).hasSize(1);
assertThat(pending.get(0).getStatus()).isEqualTo("PENDING");
}
@Test
void shouldSupportPostgresSpecificFeatures() {
// Can use Postgres-specific features like:
// - JSONB columns
// - Array types
// - Full-text search
}
}If not using @ServiceConnection:
@SpringBootTest
@Testcontainers
class OrderServiceTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
@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);
}
}| Database | Container Class | Maven Artifact |
|---|---|---|
| PostgreSQL | PostgreSQLContainer | testcontainers-postgresql |
| MySQL | MySQLContainer | testcontainers-mysql |
| MariaDB | MariaDBContainer | testcontainers-mariadb |
| SQL Server | MSSQLServerContainer | testcontainers-mssqlserver |
| Oracle | OracleContainer | testcontainers-oracle-free |
| MongoDB | MongoDBContainer | testcontainers-mongodb |
- Use @ServiceConnection when possible (Spring Boot 3.1+)
- Enable container reuse for faster local builds
- Use specific versions (postgres:18) not latest
- Keep container config in static field
- Use @DataJpaTest with AutoConfigureTestDatabase.Replace.NONE