skip to content

How do you stub a @MockitoBean, and what happens to its stubs and recorded interactions between test methods?

level: middleimportance: must knowfreq 38%

answer

  1. when().thenReturn() / given().willReturn()
  2. unstubbed = null / 0 / empty
  3. MockitoResetTestExecutionListener
  4. default MockReset.AFTER — reset each method
  5. same cached mock reused → reset keeps isolation

basics

~10 s

Stub with Mockito: when(mock.method(args)).thenReturn(value) (or given(...).willReturn(...)). By default Spring resets the mock after each test method, so stubs and verify() history do not leak between tests.

solid answer

~40 s

Because @MockitoBean produces an ordinary Mockito mock, you stub it with the normal Mockito API inside each test: when(mock.find(id)).thenReturn(...), thenThrow(...), or BDD-style given(...).willReturn(...); you verify with verify(mock). Unstubbed methods return Mockito defaults (null, 0, empty collection/Optional). Crucially, Spring's MockitoResetTestExecutionListener resets every @MockitoBean and @MockitoSpyBean around each test method — the default strategy is MockReset.AFTER (reset after the method). That means stubs you set up in one @Test and any recorded invocations do not bleed into the next method, so tests stay independent without you writing teardown. You can change the timing per field with @MockitoBean(reset = MockReset.BEFORE) or MockReset.NONE, though NONE is rarely wanted because it reintroduces cross-test coupling. Put shared stubbing in @BeforeEach if several tests need the same baseline.

code

java · 26 lines
java
@SpringBootTest
class InventoryServiceTest {

    @MockitoBean
    WarehouseClient warehouse;

    @Autowired
    InventoryService service;

    @BeforeEach
    void baseline() {
        when(warehouse.regionOf("EU")).thenReturn(Region.EU); // shared stub
    }

    @Test
    void reservesWhenInStock() {
        when(warehouse.stock("sku-1")).thenReturn(5);
        assertThat(service.reserve("sku-1", 3)).isTrue();
    }

    @Test
    void failsWhenOutOfStock() {
        // No stub for stock("sku-9") -> Mockito default 0; prior stubs were reset
        assertThat(service.reserve("sku-9", 1)).isFalse();
    }
}

go deeper

for a junior

Know the when().thenReturn() syntax and that mocks are reset per test.

for a middle

Name MockitoResetTestExecutionListener and the MockReset.AFTER default; explain why reset matters.

for a senior

Explain the interaction with context caching (same mock instance reused) and MockReset options.

for a principal

Reason about strictness differences vs MockitoExtension and cross-test coupling risks of MockReset.NONE.

## Stubbing a @MockitoBean The object injected into the annotated field is a **plain Mockito mock**, so you use Mockito's normal API — there is nothing Spring-specific about the stubbing itself: - `when(mock.call(args)).thenReturn(value)` - `when(mock.call(args)).thenThrow(new SomeException())` - BDD aliases: `given(mock.call(args)).willReturn(value)` / `willThrow(...)` - Argument matchers: `any()`, `eq(...)`, `argThat(...)` — but if you use *any* matcher in a call, **all** arguments must be matchers. - Verification: `verify(mock).call(args)`, `verify(mock, times(2))...`, `verifyNoInteractions(mock)`. ## Default return values (no stub) An unstubbed method returns Mockito's default answer (`RETURNS_DEFAULTS`): `null` for object types, `0`/`false` for primitives, and **empty** collections / `Optional.empty()` for those return types. This is why a test can pass 'accidentally' if you forgot to stub — the mock silently returns null. ## Reset between test methods — the key detail Spring registers **`MockitoResetTestExecutionListener`** automatically. It **resets** all `@MockitoBean` / `@MockitoSpyBean` mocks around each test method. The reset: - Clears all stubs configured with `when(...)`. - Clears the recorded invocation history used by `verify(...)`. The **default strategy is `MockReset.AFTER`** — the mock is reset *after* each test method completes. You can override it per field: ```java @MockitoBean(reset = MockReset.BEFORE) // reset before each method @MockitoBean(reset = MockReset.NONE) // never auto-reset (risky) ``` Because the **same ApplicationContext (and therefore the same mock instance) is cached and reused** across test methods and often across test classes, without this reset the stubs and call history *would* leak. The listener is what preserves test isolation despite the shared, cached mock. If you set `MockReset.NONE`, you are responsible for cleaning up, and cross-test coupling becomes a real bug source. ## Practical patterns - Common baseline stubbing → `@BeforeEach`. - Per-test overrides → inside the `@Test` method. - Don't rely on stubs from a previous method; assume each method starts with a fresh mock. ## Gotcha: strictness Mockito's default strictness (`STRICT_STUBS` under JUnit 5's MockitoExtension) flags unnecessary stubs — but `@MockitoBean` mocks are managed by Spring, not the MockitoExtension, so that lenient/strict behaviour differs from a plain `@Mock` setup. Don't assume unnecessary-stub detection fires on `@MockitoBean` mocks the way it does with `@ExtendWith(MockitoExtension.class)`.

  • Where does the reset behaviour come from and what is the default?
    Spring's MockitoResetTestExecutionListener, registered automatically. The default reset strategy is MockReset.AFTER — the mock's stubs and invocation history are cleared after each test method.
  • Why is resetting necessary given each @Test looks independent?
    The ApplicationContext (and thus the mock instance) is cached and reused across methods/classes, so the same mock object persists. Without reset, stubs and verify history would leak between tests.

saying these in an interview costs you the question

  • Assuming each test method gets a brand-new mock object (it is the same cached instance, merely reset).
  • Saying stubs carry over between test methods by default.
  • Forgetting that unstubbed methods return null/0/empty rather than throwing.

context