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.
207 lines
5.8 KiB
Markdown
207 lines
5.8 KiB
Markdown
# MockMvc Classic
|
|
|
|
Traditional MockMvc API for Spring MVC controller tests (pre-Spring Boot 3.2 or legacy codebases).
|
|
|
|
## When to Use This Reference
|
|
|
|
- The project uses Spring Boot < 3.2 (no `MockMvcTester` available)
|
|
- Existing tests use `mvc.perform(...)` and you are maintaining or extending them
|
|
- You need to migrate classic MockMvc tests to `MockMvcTester` (see migration section below)
|
|
- The user explicitly asks about `ResultActions`, `andExpect()`, or Hamcrest-style web assertions
|
|
|
|
For new tests on Spring Boot 3.2+, prefer [mockmvc-tester.md](mockmvc-tester.md) instead.
|
|
|
|
## Setup
|
|
|
|
```java
|
|
@WebMvcTest(OrderController.class)
|
|
class OrderControllerTest {
|
|
|
|
@Autowired
|
|
private MockMvc mvc;
|
|
|
|
@MockBean
|
|
private OrderService orderService;
|
|
}
|
|
```
|
|
|
|
## Basic GET Request
|
|
|
|
```java
|
|
@Test
|
|
void shouldReturnOrder() throws Exception {
|
|
given(orderService.findById(1L)).willReturn(new Order(1L, "PENDING", 99.99));
|
|
|
|
mvc.perform(get("/orders/1"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
|
.andExpect(jsonPath("$.id").value(1))
|
|
.andExpect(jsonPath("$.status").value("PENDING"))
|
|
.andExpect(jsonPath("$.totalToPay").value(99.99));
|
|
}
|
|
```
|
|
|
|
## POST with Request Body
|
|
|
|
```java
|
|
@Test
|
|
void shouldCreateOrder() throws Exception {
|
|
given(orderService.create(any(OrderRequest.class))).willReturn(1L);
|
|
|
|
mvc.perform(post("/orders")
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content("{\"product\": \"Laptop\", \"quantity\": 2}"))
|
|
.andExpect(status().isCreated())
|
|
.andExpect(header().string("Location", "/orders/1"));
|
|
}
|
|
```
|
|
|
|
## PUT Request
|
|
|
|
```java
|
|
@Test
|
|
void shouldUpdateOrder() throws Exception {
|
|
mvc.perform(put("/orders/1")
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content("{\"status\": \"COMPLETED\"}"))
|
|
.andExpect(status().isOk());
|
|
}
|
|
```
|
|
|
|
## DELETE Request
|
|
|
|
```java
|
|
@Test
|
|
void shouldDeleteOrder() throws Exception {
|
|
mvc.perform(delete("/orders/1"))
|
|
.andExpect(status().isNoContent());
|
|
}
|
|
```
|
|
|
|
## Status Matchers
|
|
|
|
```java
|
|
.andExpect(status().isOk()) // 200
|
|
.andExpect(status().isCreated()) // 201
|
|
.andExpect(status().isNoContent()) // 204
|
|
.andExpect(status().isBadRequest()) // 400
|
|
.andExpect(status().isUnauthorized()) // 401
|
|
.andExpect(status().isForbidden()) // 403
|
|
.andExpect(status().isNotFound()) // 404
|
|
.andExpect(status().is(422)) // arbitrary code
|
|
```
|
|
|
|
## JSON Path Assertions
|
|
|
|
```java
|
|
// Exact value
|
|
.andExpect(jsonPath("$.status").value("PENDING"))
|
|
|
|
// Existence
|
|
.andExpect(jsonPath("$.id").exists())
|
|
.andExpect(jsonPath("$.deletedAt").doesNotExist())
|
|
|
|
// Array size
|
|
.andExpect(jsonPath("$.items").isArray())
|
|
.andExpect(jsonPath("$.items", hasSize(3)))
|
|
|
|
// Nested field
|
|
.andExpect(jsonPath("$.customer.name").value("John Doe"))
|
|
.andExpect(jsonPath("$.customer.address.city").value("Berlin"))
|
|
|
|
// With Hamcrest matchers
|
|
.andExpect(jsonPath("$.total", greaterThan(0.0)))
|
|
.andExpect(jsonPath("$.description", containsString("order")))
|
|
```
|
|
|
|
## Content Assertions
|
|
|
|
```java
|
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
|
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
|
|
.andExpect(content().string(containsString("PENDING")))
|
|
.andExpect(content().json("{\"status\":\"PENDING\"}"))
|
|
```
|
|
|
|
## Header Assertions
|
|
|
|
```java
|
|
.andExpect(header().string("Location", "/orders/1"))
|
|
.andExpect(header().string("Content-Type", containsString("application/json")))
|
|
.andExpect(header().exists("X-Request-Id"))
|
|
.andExpect(header().doesNotExist("X-Deprecated"))
|
|
```
|
|
|
|
## Request Parameters and Headers
|
|
|
|
```java
|
|
// Query parameters
|
|
mvc.perform(get("/orders").param("status", "PENDING").param("page", "0"))
|
|
.andExpect(status().isOk());
|
|
|
|
// Path variables
|
|
mvc.perform(get("/orders/{id}", 1L))
|
|
.andExpect(status().isOk());
|
|
|
|
// Request headers
|
|
mvc.perform(get("/orders/1").header("X-Api-Key", "secret"))
|
|
.andExpect(status().isOk());
|
|
```
|
|
|
|
## Capturing the Response
|
|
|
|
```java
|
|
@Test
|
|
void shouldReturnCreatedId() throws Exception {
|
|
given(orderService.create(any())).willReturn(42L);
|
|
|
|
MvcResult result = mvc.perform(post("/orders")
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
.content("{\"product\": \"Laptop\", \"quantity\": 1}"))
|
|
.andExpect(status().isCreated())
|
|
.andReturn();
|
|
|
|
String location = result.getResponse().getHeader("Location");
|
|
assertThat(location).isEqualTo("/orders/42");
|
|
}
|
|
```
|
|
|
|
## Chaining with andDo
|
|
|
|
```java
|
|
mvc.perform(get("/orders/1"))
|
|
.andDo(print()) // prints request/response to console (debug)
|
|
.andExpect(status().isOk());
|
|
```
|
|
|
|
## Static Imports
|
|
|
|
```java
|
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
|
|
import static org.hamcrest.Matchers.*;
|
|
```
|
|
|
|
## Migration to MockMvcTester
|
|
|
|
| Classic MockMvc | MockMvcTester (recommended) |
|
|
| --- | --- |
|
|
| `@Autowired MockMvc mvc` | `@Autowired MockMvcTester mvc` |
|
|
| `mvc.perform(get("/orders/1"))` | `mvc.get().uri("/orders/1")` |
|
|
| `.andExpect(status().isOk())` | `.hasStatusOk()` |
|
|
| `.andExpect(jsonPath("$.status").value("X"))` | `.bodyJson().convertTo(T.class)` + AssertJ |
|
|
| `throws Exception` on every method | No checked exception |
|
|
| Hamcrest matchers | AssertJ fluent assertions |
|
|
|
|
See [mockmvc-tester.md](mockmvc-tester.md) for the full modern API.
|
|
|
|
## Key Points
|
|
|
|
1. **Every test method must declare `throws Exception`** — `perform()` throws checked exceptions
|
|
2. **Use `andDo(print())` during debugging** — remove before committing
|
|
3. **Prefer `jsonPath()` over `content().string()`** — more precise field-level assertions
|
|
4. **Static imports are required** — IDE can auto-add them
|
|
5. **Migrate to MockMvcTester** when upgrading to Spring Boot 3.2+ for better readability
|