java 88 lines · 3 tabs

Testing a Spring Boot Controller Slice with @WebMvcTest, MockMvc and a Mocked Service

Shared by codesnips Aug 2026
3 tabs
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> getOrder(@PathVariable long id) {
        return orderService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Order createOrder(@Valid @RequestBody CreateOrderRequest request) {
        return orderService.create(request.customer(), request.amount());
    }
}
3 files · java Explain with highlit

This snippet demonstrates the controller-slice testing pattern in Spring Boot, where only the web layer is loaded and everything below it is mocked. In OrderController, the controller depends on an OrderService and exposes two endpoints: a GET /api/orders/{id} that returns a single order and a POST /api/orders that validates a request body and creates one. The controller stays thin — it delegates business logic to the service and only concerns itself with HTTP status codes, path variables, and JSON binding.

The request payload in CreateOrderRequest carries Jakarta Bean Validation annotations (@NotBlank, @Positive), and the controller opts into them with @Valid. This matters for the test because @WebMvcTest boots the full MVC machinery — message converters, argument resolvers, and the validation pipeline — so validation failures are exercised for real rather than stubbed.

The heart of the example is OrderControllerTest, annotated with @WebMvcTest(OrderController.class). That annotation loads a narrow context containing just that controller plus MVC infrastructure; it deliberately excludes services, repositories, and @Component beans, which keeps the test fast and focused. Because the real OrderService is not in the context, it is supplied as a @MockBean, letting Mockito control its behavior per test.

The injected MockMvc sends simulated requests without starting a servlet container. In returnsOrderWhenFound, given(...).willReturn(...) stubs the service, and the fluent andExpect chain asserts the status and drills into the JSON body with jsonPath. In returnsNotFoundWhenMissing, the service returns an empty Optional so the controller's mapping to 404 is verified. The rejectsInvalidPayload test posts a body that violates the constraints and expects 400, confirming the validation wiring end to end.

The trade-off is scope: this approach proves the HTTP contract, serialization, and validation, but not the service internals or the database — those belong in separate unit or @SpringBootTest integration tests. Reaching for @WebMvcTest is ideal when the goal is fast, targeted feedback on a single controller's request and response behavior without the cost of a full application context.


Related snips

Share this code

Here's the card — post it anywhere.

Testing a Spring Boot Controller Slice with @WebMvcTest, MockMvc and a Mocked Service — share card
Link copied