Testing 404 in @WebMvcTest
Fast recall
Quick revision
Core answer
status().isNotFound() plus the public error body. The application must map the condition to 404, but @ControllerAdvice is only one option; a controller may use ResponseStatusException, or an exception may carry @ResponseStatus. The test should exercise the mapping the application actually uses.Ability to test error paths and exception handling in Spring controllers—not just the happy path where everything succeeds
Writing tests that only verify successful cases and forgetting to test edge cases like missing resources, which are critical for production-ready APIs
Explain that without @ControllerAdvice or @ExceptionHandler, the exception becomes a 500 Internal Server Error—showing you understand Spring's exception handling chain
Interview-ready guide
Interview answer
I arrange the application's missing-resource path, perform the request with MockMvc, and assert 404 plus the public error body. If the service throws ResourceNotFoundException, the MVC test must include the real mapping—perhaps @ControllerAdvice, an annotation on the exception, or another configured handler. A plain custom exception is not automatically a 404. I also keep the resource contract precise: a missing item may be 404, while an empty collection often remains 200 with []. The test verifies status, content type, and safe client-facing fields without exposing internal exception details.
Testing 404 in @WebMvcTest
Direct Answer
Arrange the not-found path, call it with MockMvc, and assert status().isNotFound() plus the public error body. The application must map the condition to 404, but @ControllerAdvice is only one option; a controller may use ResponseStatusException, or an exception may carry @ResponseStatus. The test should exercise the mapping the application actually uses.
Code Example
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private OrderService orderService;
@Test
void shouldReturn404WhenOrderNotFound() throws Exception {
var orderId = 999L;
// Stub service to throw exception
when(orderService.findById(orderId))
.thenThrow(new ResourceNotFoundException("Order not found"));
mockMvc.perform(get("/api/orders/{id}", orderId))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.message").value("Order not found"));
}
}
@ControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) // Maps to 404
public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
return new ErrorResponse(ex.getMessage());
}
}
record ErrorResponse(String message) {}
Common Mistakes & Pitfalls
- Assuming a custom exception automatically becomes 404: Include the application's
@ControllerAdvice,@ResponseStatus,ResponseStatusException, or direct mapping in the test boundary. - Returning 404 for every empty result: A missing item and an empty collection are different API contracts.
- Exposing exception details: Check a stable public error body rather than a stack trace, SQL message, or internal class name.
- Testing only success: A not-found branch can have different advice, serialization, and security behavior and deserves its own MVC test.
Check your understanding
Answer these without looking back at the article.
- What is @ControllerAdvice and how does it work with @ExceptionHandler?
- How would you test a 400 Bad Request from a validation failure?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.