- 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.
5.4 KiB
Test Slices Overview
Quick reference for selecting the right Spring Boot test slice.
Decision Matrix
| Annotation | Use When | Loads | Speed |
|---|---|---|---|
| None (plain JUnit) | Testing pure business logic | Nothing | Fastest |
@WebMvcTest |
Controller + HTTP layer | Controllers, MVC, Jackson | Fast |
@DataJpaTest |
Repository queries | Repositories, JPA, DataSource | Fast |
@RestClientTest |
REST client code | RestTemplate/RestClient, Jackson | Fast |
@JsonTest |
JSON serialization | ObjectMapper only | Fastest slice |
@WebFluxTest |
Reactive controllers | Controllers, WebFlux | Fast |
@DataJdbcTest |
JDBC repositories | Repositories, JDBC | Fast |
@DataMongoTest |
MongoDB repositories | Repositories, MongoDB | Fast |
@DataRedisTest |
Redis repositories | Repositories, Redis | Fast |
@SpringBootTest |
Full integration | Entire application | Slow |
Selection Guide
Use NO Annotation (Plain Unit Test)
class PriceCalculatorTest {
private PriceCalculator calculator = new PriceCalculator();
@Test
void shouldApplyDiscount() {
var result = calculator.applyDiscount(100, 0.1);
assertThat(result).isEqualTo(new BigDecimal("90.00"));
}
}
When: Pure business logic, no dependencies or simple dependencies mockable via constructor injection.
Use @WebMvcTest
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired private MockMvcTester mvc;
@MockitoBean private OrderService orderService;
}
When: Testing request mapping, validation, JSON mapping, security, filters.
What you get: MockMvc, ObjectMapper, Spring Security (if present), exception handlers.
Use @DataJpaTest
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
}
When: Testing custom JPA queries, entity mappings, transaction behavior, cascade operations.
What you get: Repository beans, EntityManager, TestEntityManager, transaction support.
Use @RestClientTest
@RestClientTest(WeatherService.class)
class WeatherServiceTest {
@Autowired private WeatherService weatherService;
@Autowired private MockRestServiceServer server;
}
When: Testing REST clients that call external APIs.
What you get: MockRestServiceServer to stub HTTP responses.
Use @JsonTest
@JsonTest
class OrderJsonTest {
@Autowired private JacksonTester<Order> json;
}
When: Testing custom serializers/deserializers, complex JSON mapping.
Use @SpringBootTest
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class OrderIntegrationTest {
@Autowired private RestTestClient restClient;
}
When: Testing full request flow, security filters, database interactions together.
What you get: Full application context, embedded server (optional), real beans.
Common Mistakes
- Using @SpringBootTest for everything - Slows down your test suite unnecessarily
- @WebMvcTest without mocking services - Causes context loading failures
- @DataJpaTest with @MockBean - Defeats the purpose (you want real repositories)
- Multiple slices in one test - Each slice is a separate test class
Java 25 Features in Tests
Records for Test Data
record OrderRequest(String product, int quantity) {}
record OrderResponse(Long id, String status, BigDecimal total) {}
Pattern Matching in Tests
@Test
void shouldHandleDifferentOrderTypes() {
var order = orderService.create(new OrderRequest("Product", 2));
switch (order) {
case PhysicalOrder po -> assertThat(po.getShippingAddress()).isNotNull();
case DigitalOrder do_ -> assertThat(do_.getDownloadLink()).isNotNull();
default -> throw new IllegalStateException("Unknown order type");
}
}
Text Blocks for JSON
@Test
void shouldParseComplexJson() {
var json = """
{
"id": 1,
"status": "PENDING",
"items": [
{"product": "Laptop", "price": 999.99},
{"product": "Mouse", "price": 29.99}
]
}
""";
assertThat(mvc.post().uri("/orders")
.contentType(APPLICATION_JSON)
.content(json))
.hasStatus(CREATED);
}
Sequenced Collections
@Test
void shouldReturnOrdersInSequence() {
var orders = orderRepository.findAll();
assertThat(orders.getFirst().getStatus()).isEqualTo("NEW");
assertThat(orders.getLast().getStatus()).isEqualTo("COMPLETED");
assertThat(orders.reversed().getFirst().getStatus()).isEqualTo("COMPLETED");
}
Dependencies by Slice
<!-- WebMvcTest -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<!-- DataJpaTest -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- RestClientTest -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-restclient-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Testcontainers -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>