# Web Layer Tests With @WebMvcTest — Spring Boot

Source: https://www.skillbyai.com/en/spring-boot/t-web

> Controllers without the rest of the app.

## MockMvc and @MockitoBean

`@WebMvcTest(ProductController.class)` starts only the web layer (controllers, advice, JSON, validation) and provides **MockMvc** to perform requests without a real server. Dependencies such as services are replaced with mocks using `@MockitoBean` (the replacement for the older `@MockBean`). Assert status codes and JSON with jsonPath. These tests are fast and focus on HTTP behaviour: mapping, validation, error handling and serialisation. In Spring Boot 4 the annotation lives in the webmvc test module.

## Fast slices, real assertions

Slice tests start only the layer under test; a common pitfall shows why bean placement matters.

![Three ideas: web slice tests, data slice tests, the context-loading pitfall.](assets/figures/spring-boot/section-5-map.svg) — Figure 5.1 — Web slices, data slices and pitfalls.

## Controller tests with MockMvc

This file is from a demo "shop" service generated by start.spring.io for Spring Boot 4.1.1 and Java 21 (Temurin 21.0.12), built with Maven 3.9.16; the project compiled and its tests passed. Three tests cover a successful lookup, a 404 problem detail and a rejected invalid body.

```java
package com.example.shop;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.math.BigDecimal;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(ProductController.class)               // only the web layer is started
@EnableConfigurationProperties(ShopProperties.class)
class ProductControllerTest {

    @Autowired MockMvc mvc;
    @MockitoBean ProductService service;           // the service is replaced by a mock

    @Test
    void returnsProduct() throws Exception {
        given(service.get(1L)).willReturn(new Product("Notebook", new BigDecimal("120.00")));
        mvc.perform(get("/api/products/1"))
           .andExpect(status().isOk())
           .andExpect(jsonPath("$.name").value("Notebook"))
           .andExpect(jsonPath("$.currency").value("INR"));
    }

    @Test
    void unknownProductIs404ProblemDetail() throws Exception {
        given(service.get(99L)).willThrow(new ProductNotFoundException(99));
        mvc.perform(get("/api/products/99"))
           .andExpect(status().isNotFound())
           .andExpect(jsonPath("$.title").value("Product not found"));
    }

    @Test
    void rejectsInvalidBody() throws Exception {
        mvc.perform(post("/api/products").contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\": \"\", \"price\": 0}"))
           .andExpect(status().isBadRequest());
    }
}
```

## Running the tests, run

I ran mvn test (Maven 3.9.16, Java 21) on the demo; the summary lines are copied from that run.

```bash
mvn test
```

Output:

```
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.749 s -- in com.example.shop.ProductRepositoryTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.423 s -- in com.example.shop.ProductControllerTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
```

**Quiz:** What does @WebMvcTest start?

- [ ] Only the JPA layer
- [ ] The whole application including the database
- [ ] Nothing; it is a unit test without Spring
- [x] Only the web layer, with MockMvc and mocked dependencies

*Answer:* Only the web layer, with MockMvc and mocked dependencies. A focused slice.
