Testing the Web Layer with @WebMvcTest
Testing the Web Layer with @WebMvcTest
When you write a REST controller you care about three things: does the endpoint respond to the right HTTP method and URL, does it map the request body and path variables correctly, and does it return the right status code and JSON? You do not care whether the database is reachable. Loading the entire application context — Hibernate, connection pools, message brokers — to answer those three questions is slow, fragile, and unnecessary. That is exactly the problem @WebMvcTest solves.
What Is a Slice Test?
Spring Boot's test slices load only a well-defined subset of the application context. @WebMvcTest is the slice for the web layer: it brings up Spring MVC infrastructure (filters, argument resolvers, message converters, DispatcherServlet) but deliberately excludes @Service, @Repository, @Component, and JPA/datasource auto-configuration. The result is a context that starts in hundreds of milliseconds instead of several seconds, and that never touches a database.
@SpringBootTest integration test loads the full context. @WebMvcTest sits in the middle: you get real MVC dispatch (deserialization, validation, exception handlers) without the full context cost.
Setting Up @WebMvcTest
Suppose you have a simple REST controller for managing products:
To test this controller in isolation, annotate the test class with @WebMvcTest and specify the controller under test:
What @WebMvcTest Provides Automatically
- A fully configured
MockMvcbean — no need to callMockMvcBuilders.standaloneSetup(). - Jackson
ObjectMapperconfigured exactly as in production (custom serializers, module registration, etc.). - All
@ControllerAdviceclasses in the application are loaded — exception handling is tested end-to-end. - Filters registered as Spring beans are applied (security filters, CORS, etc.).
- Bean Validation (
@Valid) is wired and fires during request processing.
Scoping the Slice: Which Controllers Are Loaded?
When you write @WebMvcTest(ProductController.class), only that controller is added to the context. If you omit the value — @WebMvcTest with no argument — Spring loads all @RestController and @Controller beans found in your packages. That also means all their dependencies must be mocked. Prefer the explicit form to keep tests narrow and fast.
@WebMvcTest(MyController.class). As the application grows, the no-argument form silently pulls in new controllers and their transitive @MockBean requirements, slowing the build and making failures harder to diagnose.
Testing Bean Validation
Because @Valid is processed by the MVC layer, a @WebMvcTest is the right place to verify that invalid input is rejected:
This test does not require a running service or database, yet it proves that your @NotBlank constraint on CreateProductRequest.name is wired up and will return a 400 when violated.
Security in @WebMvcTest
If your project includes spring-boot-starter-security, @WebMvcTest applies your security configuration automatically. Endpoints that require authentication will return 401 or 403 in tests. You have several options:
- Use
@WithMockUser(fromspring-security-test) to run a request as a specific authenticated user. - Provide a dedicated
@TestConfigurationthat relaxes security for the test context. - Call
.with(SecurityMockMvcRequestPostProcessors.csrf())on POST/PUT requests when CSRF is enabled.
excludeAutoConfiguration, you will stop testing the security contract of your API. Use @WithMockUser or a test-specific security config that retains realistic rules while allowing test execution.
Performance Trade-offs
The web slice is fast precisely because it is narrow. But that narrowness has consequences: your service and repository layers are completely absent, so you must mock every dependency your controller uses with @MockBean. If a controller has many collaborators this can become verbose. The boundary signals a design concern: controllers with many direct service dependencies are harder to test and harder to reason about.
Another trade-off is context caching. Spring Boot caches the application context across tests that share the same configuration. Adding different @MockBean definitions in different test classes creates different context configurations, which defeats caching and slows the suite. Group controllers with the same mock requirements into one test class where practical.
Summary
@WebMvcTest gives you a fast, focused slice test for the web layer: real MVC dispatch, real Jackson serialization, real Bean Validation, and real exception handlers — with zero database cost. Declare the specific controller under test, replace all services with @MockBean, and use MockMvc to make HTTP-level assertions. The next lesson takes a deeper look at MockMvc itself and the rich set of assertions and customizations it provides.