feat: add spring-boot-testing skill for Spring Boot 4.0 (#1085)

- Introduced MockMvcTester for AssertJ-style assertions in Spring MVC testing.
- Added @RestClientTest for testing REST clients with MockRestServiceServer.
- Implemented RestTestClient as a modern alternative to TestRestTemplate.
- Documented migration steps from Spring Boot 3.x to 4.0, including dependency and annotation changes.
- Created an overview of test slices to guide testing strategies.
- Included Testcontainers setup for JDBC testing with PostgreSQL and MySQL.
- Enhanced @WebMvcTest documentation with examples for various HTTP methods and validation.
This commit is contained in:
Kartik Dhiman
2026-03-20 04:54:37 +05:30
committed by GitHub
parent ee71c0689c
commit e4fc57f204
16 changed files with 3171 additions and 0 deletions

View File

@@ -0,0 +1,234 @@
# Testcontainers JDBC
Testing JPA repositories with real databases using Testcontainers.
## Overview
Testcontainers provides real database instances in Docker containers for integration testing. More reliable than H2 for production parity.
## PostgreSQL Setup
### Dependencies
```xml
<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>
```
### Basic Test
```java
@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;
}
```
## MySQL Setup
```xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-mysql</artifactId>
<scope>test</scope>
</dependency>
```
```java
@Container
@ServiceConnection
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.4");
```
## Multiple Databases
```java
@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");
}
```
## Container Reuse (Speed Optimization)
Add to `~/.testcontainers.properties`:
```properties
testcontainers.reuse.enable=true
```
Then enable reuse in code:
```java
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.withReuse(true);
```
## Database Initialization
### With SQL Scripts
```java
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.withInitScript("schema.sql");
```
### With Flyway
```java
@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
}
}
```
## Advanced Configuration
### Custom Database/Schema
```java
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.withDatabaseName("testdb")
.withUsername("testuser")
.withPassword("testpass")
.withInitScript("init-schema.sql");
```
### Wait Strategies
```java
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18")
.waitingFor(Wait.forLogMessage(".*database system is ready.*", 1));
```
## Test Example
```java
@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
}
}
```
## @DynamicPropertySource Alternative
If not using @ServiceConnection:
```java
@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);
}
}
```
## Supported Databases
| 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 |
## Best Practices
1. Use @ServiceConnection when possible (Spring Boot 3.1+)
2. Enable container reuse for faster local builds
3. Use specific versions (postgres:18) not latest
4. Keep container config in static field
5. Use @DataJpaTest with AutoConfigureTestDatabase.Replace.NONE