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.
5.8 KiB
5.8 KiB
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
MockMvcTesteravailable) - 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 instead.
Setup
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private OrderService orderService;
}
Basic GET Request
@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
@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
@Test
void shouldUpdateOrder() throws Exception {
mvc.perform(put("/orders/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"status\": \"COMPLETED\"}"))
.andExpect(status().isOk());
}
DELETE Request
@Test
void shouldDeleteOrder() throws Exception {
mvc.perform(delete("/orders/1"))
.andExpect(status().isNoContent());
}
Status Matchers
.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
// 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
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(content().string(containsString("PENDING")))
.andExpect(content().json("{\"status\":\"PENDING\"}"))
Header Assertions
.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
// 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
@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
mvc.perform(get("/orders/1"))
.andDo(print()) // prints request/response to console (debug)
.andExpect(status().isOk());
Static Imports
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 for the full modern API.
Key Points
- Every test method must declare
throws Exception—perform()throws checked exceptions - Use
andDo(print())during debugging — remove before committing - Prefer
jsonPath()overcontent().string()— more precise field-level assertions - Static imports are required — IDE can auto-add them
- Migrate to MockMvcTester when upgrading to Spring Boot 3.2+ for better readability