Web Slice Tests with @WebMvcTest
Test controllers in isolation with MockMvc - routing, validation, status codes, and JSON - without a database.
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());
}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.
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.