Files
awesome-copilot/skills/spring-boot-testing/references/webmvctest.md
Kartik Dhiman e4fc57f204 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.
2026-03-20 10:24:37 +11:00

178 lines
3.6 KiB
Markdown

# @WebMvcTest
Testing Spring MVC controllers with focused slice tests.
## Basic Structure
```java
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired
private MockMvcTester mvc;
@MockitoBean
private OrderService orderService;
@MockitoBean
private UserService userService;
}
```
## What Gets Loaded
- The specified controller(s)
- Spring MVC infrastructure (HandlerMapping, HandlerAdapter)
- Jackson ObjectMapper (for JSON)
- Exception handlers (@ControllerAdvice)
- Spring Security filters (if on classpath)
- Validation (if on classpath)
## Testing GET Endpoints
```java
@Test
void shouldReturnOrder() {
var order = new Order(1L, "PENDING", BigDecimal.valueOf(99.99));
given(orderService.findById(1L)).willReturn(order);
assertThat(mvc.get().uri("/orders/1"))
.hasStatusOk()
.hasContentType(MediaType.APPLICATION_JSON)
.bodyJson()
.extractingPath("$.status")
.isEqualTo("PENDING");
}
```
## Testing POST with Request Body
### Using Text Blocks (Java 25)
```java
@Test
void shouldCreateOrder() {
given(orderService.create(any(OrderRequest.class))).willReturn(1L);
var json = """
{
"product": "Product A",
"quantity": 2
}
""";
assertThat(mvc.post().uri("/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.hasStatus(HttpStatus.CREATED)
.hasHeader("Location", "/orders/1");
}
```
### Using Records
```java
record OrderRequest(String product, int quantity) {}
@Test
void shouldCreateOrderWithRecord() {
var request = new OrderRequest("Product A", 2);
given(orderService.create(any())).willReturn(1L);
assertThat(mvc.post().uri("/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(json.write(request).getJson()))
.hasStatus(HttpStatus.CREATED);
}
```
## Testing Validation Errors
```java
@Test
void shouldRejectInvalidOrder() {
var invalidJson = """
{
"product": "",
"quantity": -1
}
""";
assertThat(mvc.post().uri("/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidJson))
.hasStatus(HttpStatus.BAD_REQUEST)
.bodyJson()
.hasPath("$.errors");
}
```
## Testing Query Parameters
```java
@Test
void shouldFilterOrdersByStatus() {
assertThat(mvc.get().uri("/orders?status=PENDING"))
.hasStatusOk();
verify(orderService).findByStatus(OrderStatus.PENDING);
}
```
## Testing Path Variables
```java
@Test
void shouldCancelOrder() {
assertThat(mvc.put().uri("/orders/123/cancel"))
.hasStatusOk();
verify(orderService).cancel(123L);
}
```
## Testing with Security
```java
@Test
@WithMockUser(roles = "ADMIN")
void adminShouldDeleteOrder() {
assertThat(mvc.delete().uri("/orders/1"))
.hasStatus(HttpStatus.NO_CONTENT);
}
@Test
void anonymousUserShouldBeForbidden() {
assertThat(mvc.delete().uri("/orders/1"))
.hasStatus(HttpStatus.UNAUTHORIZED);
}
```
## Multiple Controllers
```java
@WebMvcTest({OrderController.class, ProductController.class})
class WebLayerTest {
// Tests multiple controllers in one slice
}
```
## Excluding Auto-Configuration
```java
@WebMvcTest(OrderController.class)
@AutoConfigureMockMvc(addFilters = false) // Skip security filters
class OrderControllerWithoutSecurityTest {
// Tests without security filters
}
```
## Key Points
1. Always mock services with @MockitoBean
2. Use MockMvcTester for AssertJ-style assertions
3. Test HTTP semantics (status, headers, content-type)
4. Verify service method calls when side effects matter
5. Don't test business logic here - that's for unit tests
6. Leverage Java 25 text blocks for JSON payloads