Start Learning
Javaneer
Back to stage
Module 5·Testing Spring Applications

Web Slice Tests with @WebMvcTest

Test controllers in isolation with MockMvc - routing, validation, status codes, and JSON - without a database.

16 min readIntermediate
On this page

To test a controller you don't need a database, a real service, or a running server - you need to check that a request is routed correctly, validated, and turned into the right status and JSON. @WebMvcTest loads just the web layer and gives you MockMvc to fire requests at it.

The slice

@WebMvcTest(BookController.class) starts Spring MVC and that one controller - no services, no repositories, no database. Because the controller depends on BookService, you supply a mock with @MockitoBean:

@WebMvcTest(BookController.class)
class BookControllerWebTest {

    @Autowired MockMvc mvc;              // fires simulated HTTP requests
    @MockitoBean BookService service;    // a Mockito mock, put into the context

    @Test
    void returns404WhenBookMissing() throws Exception {
        when(service.findByIsbn("000")).thenThrow(new BookNotFoundException("000"));

        mvc.perform(get("/api/books/000"))
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.title").value("Not found"));
    }
}

MockMvc runs the request through the real MVC machinery - routing, argument binding, validation, your exception handler, JSON serialization - without a network or server.

What it's perfect for

  • Status codes — 200, 201, 404, 400.
  • Validation — POST an invalid body, expect 400 and the error details.
  • JSON shape — assert fields with jsonPath.
  • Routing & mapping — the right method handles the right URL and verb.
@Test
void rejectsInvalidNewBook() throws Exception {
    String badBody = "{\"isbn\":\"bad\",\"title\":\"\",\"author\":\"X\",\"copies\":0}";
    mvc.perform(post("/api/books").contentType(APPLICATION_JSON).content(badBody))
        .andExpect(status().isBadRequest());
}
A flight simulator for your controller

Pilots train in a flight simulator: real cockpit, real controls, real responses - but no actual aircraft, fuel, or sky. MockMvc is a flight simulator for your web layer: real routing, real validation, real JSON, but no server, network, or database. You exercise the controller under realistic conditions, fast and safely.

Mock the layer below, test the layer you're on

The point of a slice is focus. In a @WebMvcTest you @MockitoBean the service and script its responses, so a failing test points squarely at the web layer - not at service logic or a database. Test the service's logic separately, in its own test.

Test the create endpoint

Write, in words, two @WebMvcTest cases for POST /api/books: one where the service succeeds and you expect 201 Created, and one where the request body is invalid and you expect 400. What do you mock, and what do you assert in each?

In a @WebMvcTest, how is the controller's service dependency provided?

Key takeaways

  • @WebMvcTest loads only the web layer plus the target controller - no service, repository, or database.
  • MockMvc fires requests through the real MVC machinery (routing, validation, exception handling, JSON) with no server or network.
  • Supply the controller's collaborators as @MockitoBean mocks and stub them per test.
  • It's ideal for asserting status codes, validation behavior, JSON shape, and routing.
  • Keep the slice focused: test service logic in its own test, not through the controller.
Was this lesson helpful?
Edit this page on GitHub