@WebMvcTest
IntermediateSlices the context to only MVC infrastructure and the specified controller, making tests lighter; beans not in scope must be mocked with @MockBean.
Overview
@WebMvcTest is a Spring Boot test slice that loads only the MVC layer: controllers, @ControllerAdvice, filters, WebMvcConfigurer, and Jackson converters — but NOT service beans, repositories, or @Component classes. This makes the test context much lighter than @SpringBootTest while still exercising the real MVC pipeline (unlike standalone MockMvc). Any bean the controller depends on must be provided with @MockBean. Spring Security is auto-configured if spring-security is on the classpath; for security tests, use @WithMockUser or configure a custom SecurityFilterChain test bean. @WebMvcTest is the recommended approach for controller-layer unit tests in Spring Boot.
@WebMvcTest controller slice with @MockBean
Specify the controller class to test. All its dependencies must be mocked. MockMvc is auto-wired.
@WebMvcTest(OrderController.class) // only loads OrderController + MVC infrastructure
class OrderControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean // provides mock to OrderController
private OrderService orderService;
@MockBean
private OrderMapper orderMapper;
@Test
void getOrder_found_returns200() throws Exception {
OrderDto dto = new OrderDto(1L, "PENDING", new BigDecimal("99.99"));
when(orderService.findById(1L)).thenReturn(dto);
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.status").value("PENDING"));
}
@Test
void getOrder_notFound_returns404() throws Exception {
when(orderService.findById(99L))
.thenThrow(new OrderNotFoundException(99L));
mockMvc.perform(get("/api/orders/99"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.message").value("Order 99 not found"));
}
@Test
void createOrder_invalidBody_returns400() throws Exception {
// empty customerId triggers @NotBlank validation
String json = """{"customerId":"","total":50.0}""";
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isBadRequest());
}
}@WebMvcTest with Spring Security
Spring Security is included in the slice. By default all endpoints require authentication. Use @WithMockUser or disable security for specific tests.
@WebMvcTest(OrderController.class)
class OrderSecurityTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private OrderService orderService;
// Include if your SecurityConfig depends on beans not in the slice
@MockBean
private UserDetailsService userDetailsService;
@Test
@WithMockUser(username = "john", roles = "USER")
void getOrder_withUser_returns200() throws Exception {
when(orderService.findById(1L)).thenReturn(new OrderDto(1L, "PENDING", BigDecimal.TEN));
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(roles = "ADMIN")
void deleteOrder_withAdmin_returns204() throws Exception {
mockMvc.perform(delete("/api/orders/1"))
.andExpect(status().isNoContent());
}
@Test
void getOrder_unauthenticated_returns401() throws Exception {
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isUnauthorized());
}
// Exclude security for a specific test class
@Import(NoSecurityConfig.class)
static class NoSecurityConfig {
@Bean SecurityFilterChain noSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.anyRequest().permitAll());
return http.build();
}
}
}Testing @ControllerAdvice exception handlers
@WebMvcTest automatically includes @ControllerAdvice beans. Test exception handler mapping without needing the full application context.
// GlobalExceptionHandler — auto-detected by @WebMvcTest
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(OrderNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(OrderNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ValidationErrorResponse> handleValidation(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest()
.body(new ValidationErrorResponse(errors));
}
}
// Test — @ControllerAdvice is included automatically in @WebMvcTest slice
@Test
void handlerThrowsNotFoundException_returns404WithMessage() throws Exception {
when(orderService.findById(1L)).thenThrow(new OrderNotFoundException(1L));
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.message").value("Order 1 not found"));
}Key Points to Remember
- 1@WebMvcTest loads only MVC infrastructure — faster than @SpringBootTest but tests the real MVC pipeline.
- 2All service/repository beans must be provided via @MockBean; they are not auto-scanned in the slice.
- 3Spring Security IS included; use @WithMockUser, @WithAnonymousUser, or mock UserDetailsService as needed.
- 4@ControllerAdvice beans are automatically included in the slice — test exception handler mappings here.
- 5Specify the controller class in @WebMvcTest(MyController.class) to load only that controller and reduce context size.
- 6Use @Import to add custom beans or configuration to the slice without loading the full application context.
Interview Questions
Sign in to ask AriaWhat beans does @WebMvcTest load and what does it exclude?
Why would you use @WebMvcTest instead of @SpringBootTest for controller tests?
How does @WebMvcTest handle Spring Security and how do you test secured endpoints?
What is the difference between @MockBean and @Mock, and which is required in @WebMvcTest?
How would you test an endpoint that uploads a file using @WebMvcTest?
Ask Aria about @WebMvcTest
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.