MockMvc Testing
IntermediateMockMvc dispatches requests through the MVC pipeline without starting a real server, enabling fast controller tests with request/response assertions.
Overview
MockMvc simulates HTTP requests through the full Spring MVC dispatch pipeline — filters, handler mapping, argument resolvers, controllers, exception handlers — without binding to a real network port. It is significantly faster than @SpringBootTest with a real server and covers more than unit testing a controller method directly (which skips the MVC pipeline). Tests use a fluent API: perform(get("/api/orders")) → andExpect(status().isOk()) → andExpect(jsonPath("$.id").value(1)). MockMvc can be set up standalone (only the tested controller, no Spring context) for ultra-fast unit tests, or with the full Spring context (@AutoConfigureMockMvc) for integration tests.
Standalone MockMvc — controller unit test
MockMvcBuilders.standaloneSetup() creates a minimal MVC context for just one controller. Fastest option; collaborators are mocked with Mockito.
@ExtendWith(MockitoExtension.class)
class OrderControllerTest {
@Mock
private OrderService orderService;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders
.standaloneSetup(new OrderController(orderService))
.setControllerAdvice(new GlobalExceptionHandler()) // include if needed
.build();
}
@Test
void getOrder_returnsOrder() throws Exception {
Order order = new Order(1L, "PENDING", new BigDecimal("99.99"));
when(orderService.findById(1L)).thenReturn(order);
mockMvc.perform(get("/api/orders/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.status").value("PENDING"))
.andExpect(jsonPath("$.total").value(99.99));
verify(orderService).findById(1L);
}
@Test
void createOrder_validatesInput() throws Exception {
String invalidJson = """{"customerId": null, "total": -10}""";
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidJson))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors").isArray());
}
}@AutoConfigureMockMvc — full Spring context integration test
@SpringBootTest loads the full application context; @AutoConfigureMockMvc injects a configured MockMvc. Use this when you need security filters, real beans, and the full pipeline.
@SpringBootTest
@AutoConfigureMockMvc
class OrderIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean // replaces the real bean in context
private OrderService orderService;
@Test
@WithMockUser(roles = "USER") // spring-security-test
void listOrders_authenticated_returns200() throws Exception {
when(orderService.findAll(any(Pageable.class)))
.thenReturn(Page.empty());
mockMvc.perform(get("/api/orders")
.param("page", "0")
.param("size", "20"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andDo(print()); // prints request/response to console for debugging
}
@Test
void listOrders_unauthenticated_returns401() throws Exception {
mockMvc.perform(get("/api/orders"))
.andExpect(status().isUnauthorized());
}
@Test
void createOrder_returns201WithLocation() throws Exception {
CreateOrderRequest req = new CreateOrderRequest("cust-1",
new BigDecimal("99.99"));
Order created = new Order(42L, "PENDING", req.getTotal());
when(orderService.create(any())).thenReturn(created);
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isCreated())
.andExpect(header().string("Location", containsString("/api/orders/42")));
}
}Asserting JSON with jsonPath and ResultActions
MockMvc's jsonPath matcher uses Jayway JsonPath expressions. For complex responses, andReturn() gives access to the full MvcResult.
// jsonPath assertions
mockMvc.perform(get("/api/orders"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content.length()").value(3))
.andExpect(jsonPath("$.content[0].id").exists())
.andExpect(jsonPath("$.content[0].status").value("PENDING"))
.andExpect(jsonPath("$.totalElements").value(3))
.andExpect(jsonPath("$.totalPages").value(1));
// Assert response body as string
MvcResult result = mockMvc.perform(get("/api/orders/1"))
.andReturn();
String body = result.getResponse().getContentAsString();
Order order = objectMapper.readValue(body, Order.class);
assertThat(order.getStatus()).isEqualTo("PENDING");
// Test file upload
MockMultipartFile file = new MockMultipartFile(
"invoice", "invoice.pdf",
MediaType.APPLICATION_PDF_VALUE, pdfBytes);
mockMvc.perform(multipart("/api/orders/1/invoice").file(file))
.andExpect(status().isOk());Key Points to Remember
- 1Standalone setup tests the controller in isolation (no Spring context) — fastest; use for unit tests.
- 2@AutoConfigureMockMvc with @SpringBootTest tests the full pipeline including security filters and real beans.
- 3@MockBean replaces a real Spring bean in the context with a Mockito mock — needed when using @SpringBootTest.
- 4@WithMockUser (spring-security-test) injects a mock authentication into the SecurityContext for security tests.
- 5andDo(print()) logs the full request/response to stdout — invaluable during test development.
- 6jsonPath uses Jayway JsonPath syntax: $.content[0].id, $.totalElements, $.content.length().
Interview Questions
Sign in to ask AriaWhat is the difference between standaloneSetup and @AutoConfigureMockMvc in MockMvc tests?
How do you test a secured endpoint with MockMvc without a real user in the database?
What is the difference between @MockBean and @Mock in Spring Boot tests?
How would you test file upload using MockMvc?
When would you choose @WebMvcTest over @SpringBootTest + @AutoConfigureMockMvc?
Ask Aria about MockMvc Testing
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.