mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-20 16:15:12 +00:00
- 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.
178 lines
3.6 KiB
Markdown
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
|