Home/Learn/Spring Boot/MockMvc Testing

MockMvc Testing

Intermediate
Testing

MockMvc 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.

Java — standalone MockMvc controller unit test
@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.

Java — @SpringBootTest + @AutoConfigureMockMvc integration
@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.

Java — jsonPath assertions and 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 Aria
1

What is the difference between standaloneSetup and @AutoConfigureMockMvc in MockMvc tests?

EasyAmazon
2

How do you test a secured endpoint with MockMvc without a real user in the database?

MediumNetflix
3

What is the difference between @MockBean and @Mock in Spring Boot tests?

MediumGoogle
4

How would you test file upload using MockMvc?

MediumShopify
5

When would you choose @WebMvcTest over @SpringBootTest + @AutoConfigureMockMvc?

MediumUber

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.

Loading discussion…