skip to content

In a @WebMvcTest, how do you provide the controller's service dependency, and what happens if you don't?

level: middleimportance: must knowfreq 74%

answer

  1. @MockitoBean replaces @MockBean (3.4+)
  2. missing bean → UnsatisfiedDependency/NoSuchBeanDefinition
  3. when/thenReturn to stub
  4. mock replaces real bean
  5. @MockitoSpyBean = partial

basics

~10 s

Add the missing dependency as a mock with @MockitoBean (older: @MockBean). Without it, the slice can't inject the controller's service and the context fails to start with a NoSuchBeanDefinitionException.

solid answer

~40 s

The MVC slice loads controllers but not @Service/@Repository beans, so a controller's collaborators are missing. You declare each one as a field annotated with @MockitoBean (Spring Framework 6.2 / Boot 3.4+; previously Spring Boot's @MockBean). This registers a Mockito mock in the test context and injects it into the controller; you then stub with Mockito's when(...).thenReturn(...) and verify interactions. If you omit it, Spring can't satisfy the controller's constructor dependency and startup fails with UnsatisfiedDependencyException / NoSuchBeanDefinitionException. @MockitoBean also replaces a matching real bean if one exists. Alternatives include importing a small @TestConfiguration that supplies stubs, but mocking is the idiomatic approach for a slice because the real collaborators are intentionally absent.

code

java · 18 lines
java
@WebMvcTest(OrderController.class)
class OrderControllerTest {

    @Autowired MockMvc mockMvc;

    @MockitoBean OrderService orderService; // required collaborator

    @Test
    void notFoundWhenServiceThrows() throws Exception {
        when(orderService.get(99L))
            .thenThrow(new OrderNotFoundException(99L));

        mockMvc.perform(get("/orders/99"))
               .andExpect(status().isNotFound());

        verify(orderService).get(99L);
    }
}

go deeper

for a junior

Knows you must mock the service, roughly with @MockBean/@MockitoBean.

for a middle

Knows the exact exception on omission, current @MockitoBean naming, and default mock return values.

for a senior

Distinguishes mock vs spy vs test-config stub and when each is appropriate.

for a principal

Considers the bean-override migration story and reset/isolation semantics across a large suite.

## The problem the mock solves `@WebMvcTest` builds a context containing your controller but **not** its downstream dependencies (`@Service`, `@Repository`, etc.). A typical controller is constructor-injected: ```java @RestController class UserController { private final UserService userService; UserController(UserService userService) { this.userService = userService; } } ``` Spring must find a `UserService` bean to build `UserController`. Since the service isn't loaded, the context can't be created and you get an **`UnsatisfiedDependencyException`** wrapping a **`NoSuchBeanDefinitionException`** during test startup. ## The fix: @MockitoBean Declare the collaborator as a mock: ```java @MockitoBean UserService userService; ``` `@MockitoBean` (from `org.springframework.test.context.bean.override.mockito`, added in **Spring Framework 6.2 / Spring Boot 3.4**) creates a Mockito mock, registers it as a bean, and injects it into the controller. It is the modern replacement for Spring Boot's **`@MockBean`**, which still works but is **deprecated** in 3.4+. If a real bean of that type already existed in the context, `@MockitoBean` **replaces** it. ## Stubbing and verifying With the mock in place you drive controller behavior: ```java when(userService.findById(1L)).thenReturn(new UserDto(1L, "Ada")); // ... perform request ... verify(userService).findById(1L); ``` By default an un-stubbed mock method returns Mockito defaults (null, empty collection, 0), which is a common source of surprise NPEs or empty JSON in tests. ## Related override annotations - **`@MockitoSpyBean`** (was `@SpyBean`): wraps a real bean so unstubbed calls hit the real implementation. - A **`@TestConfiguration`** with `@Bean` methods can supply hand-written stubs when you prefer real objects over mocks; import it via `@Import`. ## Gotchas - One mock per required collaborator — if a controller needs three services, all three must be provided or startup fails. - Narrowing with `@WebMvcTest(UserController.class)` avoids having to mock dependencies of unrelated controllers. - `@MockitoBean` fields no longer need to be `static` and work at class level; you can also place it on a nested config. - Resetting: Mockito mocks are reset between test methods automatically by Spring Boot's `MockitoTestExecutionListener`.

  • What is the difference between @MockitoBean and @MockitoSpyBean?
    @MockitoBean creates a full mock whose methods return defaults unless stubbed; @MockitoSpyBean wraps a real bean so unstubbed methods execute the real code, and you selectively override behavior.
  • Why was @MockBean deprecated?
    Spring Framework 6.2 introduced a first-class 'bean override' mechanism (@MockitoBean/@MockitoSpyBean) in spring-test itself, so Spring Boot's @MockBean/@SpyBean were deprecated in favor of the framework-level annotations.

saying these in an interview costs you the question

  • Saying you can @Autowired the real service in a @WebMvcTest without extra config
  • Thinking the context still starts fine without providing the dependency
  • Believing an unstubbed mock returns the real value rather than a Mockito default

context