@WebMvcTest and @MockBean in Spring Boot are your best friends for unit testing your web layer without the overhead of a full application context.

Let’s see @WebMvcTest in action. Imagine you have a UserController that interacts with a UserService.

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUser(id);
        if (user == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(user);
    }
}

And a UserService interface.

public interface UserService {
    User getUser(Long id);
}

Now, a typical @WebMvcTest for UserController:

@SpringBootTest
@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void shouldReturnUserWhenFound() throws Exception {
        User testUser = new User(1L, "Alice");
        when(userService.getUser(1L)).thenReturn(testUser);

        mockMvc.perform(get("/users/1"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.id", is(1)))
               .andExpect(jsonPath("$.name", is("Alice")));
    }

    @Test
    void shouldReturnNotFoundWhenUserDoesNotExist() throws Exception {
        when(userService.getUser(2L)).thenReturn(null);

        mockMvc.perform(get("/users/2"))
               .andExpect(status().isNotFound());
    }
}

This setup is incredibly efficient. @WebMvcTest bootstraps only the web layer components. It auto-configures MockMvc for performing simulated HTTP requests and disables SecurityAutoConfiguration and DataSourceAutoConfiguration by default, meaning you don’t get a full web server or database connection. The crucial part is @MockBean. It tells Spring to replace the actual UserService bean with a mock implementation. This isolates the UserController entirely, ensuring your tests only verify its logic, not the UserService’s. The when(userService.getUser(1L)).thenReturn(testUser); line sets up the mock behavior, defining what the userService should return when its getUser method is called with a specific argument.

The problem this solves is the fragility and slowness of integration tests. Running tests against a live server and database can be slow, prone to external failures (e.g., database availability), and make it hard to pinpoint issues within a specific component. @WebMvcTest and @MockBean allow you to test your controllers in isolation, making tests faster, more reliable, and easier to debug. You’re essentially creating a controlled environment where you can dictate the behavior of dependencies.

Internally, @WebMvcTest uses Spring Boot’s testing features to create a limited application context. It scans for @Controller, @ControllerAdvice, and WebMvcConfigurer beans. It also auto-configures MockMvc, which is a powerful tool for testing MVC controllers. MockMvc allows you to simulate HTTP requests (GET, POST, etc.) against your controllers and assert on the responses, including status codes, headers, and the response body, often using Hamcrest matchers or JSONPath. @MockBean leverages Mockito, a popular Java mocking framework, to create mock objects. When Spring Boot starts the test context, it encounters @MockBean and replaces the real bean of the specified type with the generated mock.

The exact levers you control are the beans you include in the @WebMvcTest slice and the behavior you define for your @MockBeans. You can specify which controllers to test using value = {MyController.class} or controllers = {MyController.class}. If you omit this, it will scan for all controllers in your application. You can also use @MockBean to mock any other dependency your controller might have, not just services. For instance, if your controller used a RestTemplate to call another service, you’d use @MockBean RestTemplate restTemplate;.

A common pitfall is forgetting that @WebMvcTest disables auto-configuration for certain components like DataSourceAutoConfiguration. If your controller does need to interact with a database (e.g., through a repository that’s directly injected into the controller, though this is generally discouraged), you’d need to explicitly include the necessary configurations back in. You can do this by adding the excludeAutoConfiguration attribute to @WebMvcTest and then potentially providing a test data source or using @DataJpaTest in conjunction if you need to test repository interactions.

The next concept you’ll likely explore is how to test your service layer in isolation using @ServiceTest or @DataJpaTest with @MockBean for repositories.

Want structured learning?

Take the full Spring-boot course →