skip to content

In a Mockito test the same collaborator call is stubbed twice - once in a @BeforeEach setup method and again at the top of a single test. Which stubbing wins, and what else should you know about overriding stubs?

level: seniorimportance: should knowfreq 38%

answer

  1. Stubbings scanned newest-first; last match wins
  2. Old stubbing shadowed, not deleted
  3. Fully shadowed setup stub -> UnnecessaryStubbingException
  4. Broad any() + narrow override = both used, no complaint
  5. reset(mock) is a smell; split the test

basics

~20 s

The later stubbing wins: Mockito searches registered stubbings newest-first and uses the first whose matchers fit. The setup stub still exists, and if it is never used, strict-stubs mode reports it as an UnnecessaryStubbing failure.

solid answer

~60 s

Stubbings are kept in registration order and matched **newest first**, so a second `when(repo.find(1L)).thenReturn(b)` shadows the earlier one for all subsequent calls. Nothing is deleted - the old stubbing is simply never reached while the new one matches. Three points a senior answer should add: 1. **Strictness.** With `MockitoExtension`'s default `STRICT_STUBS`, a setup stubbing that is fully shadowed before it is ever used is reported at the end as `UnnecessaryStubbingException`. That is the framework telling you the shared setup does not belong there. 2. **Overlap, not exact duplication.** A broad setup stub with `any()` plus a specific override is fine and idiomatic - the specific one is registered later, so it wins for its arguments while the broad one still covers the rest. 3. **Consecutive values.** `thenReturn(a, b)` is one stubbing yielding a then b, with the last value repeating; re-stubbing resets that sequence, it does not append to it. Prefer moving the stub into the tests that need it over `reset(mock)`, which discards all stubbings and interactions and usually signals a test doing too much.

code

java · 17 lines
java
@ExtendWith(MockitoExtension.class) // STRICT_STUBS by default
class OrderServiceTest {

    @Mock OrderRepository repo;

    @BeforeEach
    void setUp() {
        when(repo.findById(anyLong())).thenReturn(defaultOrder); // broad
    }

    @Test
    void missingOrder() {
        when(repo.findById(99L)).thenReturn(null); // narrower, registered later -> wins for 99
        assertNull(repo.findById(99L));
        assertSame(defaultOrder, repo.findById(1L)); // broad stub still used
    }
}

go deeper

for a junior

Answer the direct question: the later stubbing wins for subsequent calls.

for a middle

Add that stubbings are matched newest-first and nothing is deleted, and mention consecutive stubbing being reset rather than extended.

for a senior

Bring in strict stubs - UnnecessaryStubbingException and PotentialStubbingProblem as signals - and argue for moving stubs into the tests that need them rather than using reset().

for a principal

Discuss shared setup as a design pressure on test suites: when a fixture stops being shared it should be split, and how strictness settings shape that discipline across a codebase.

## How Mockito resolves a call Each mock keeps its stubbings in an ordered list, appended as they are registered. When a call arrives, Mockito walks that list from the **most recent** backwards and uses the first stubbing whose method and argument matchers accept the invocation. So among stubbings that match the same call, the last one registered wins - for every call after it was registered. Earlier stubbings are not removed or replaced. They stay in the list, unreachable while a newer one shadows them, and would come back into play if the newer stubbing were narrower and a differently-argued call arrived. ## Setup plus per-test override This is why the common pattern works: ```java @BeforeEach void setUp() { when(repo.findById(anyLong())).thenReturn(defaultUser); } @Test void missingUser() { when(repo.findById(99L)).thenReturn(null); // registered later -> wins for 99 ... } ``` Calls with `99L` hit the newer stubbing; every other id still falls through to the broad one. This is genuine layering and is worth keeping. ## The strictness interaction When the override is *exactly* as broad as the setup stub and the setup stub is therefore never used, Mockito's strict mode complains. With `MockitoExtension` (JUnit 5) or `MockitoJUnitRunner.Strict`, `Strictness.STRICT_STUBS` is the default, and it fails the test class with `UnnecessaryStubbingException`, naming the file and line of the unused stubbing. That is a useful signal rather than an obstacle. It is telling you the shared setup is not actually shared - some tests want a different value - so the stubbing belongs in the tests that use it. Options, in order: 1. Move the stubbing into the tests that need it (best). 2. Keep the setup stub broad and only override for specific arguments, so both are genuinely used. 3. Mark the setup stubbing `lenient()` when it truly is optional background for most tests - acceptable, but each `lenient()` is a small waiver you should be able to justify. Strict stubs also flags a related case, `PotentialStubbingProblem`: the code called a stubbed method with arguments that no stubbing matches while a stubbing for different arguments exists. That usually means the production code built its arguments differently than the test assumed - a real bug caught early, not a framework annoyance. ## Consecutive values versus re-stubbing `when(x.get()).thenReturn(a, b)` registers one stubbing that yields `a` on the first call and `b` on the second, with `b` repeating thereafter. Re-stubbing the same call registers a **new** stubbing that shadows the whole sequence from that point on; it does not extend it. If you write the same `when(...).thenReturn(...)` inside a loop expecting a queue of values, you get N stubbings where only the last is ever consulted. ## Where re-stubbing actually hurts - **Mid-test re-stubbing to change behaviour halfway through an action** makes the test read like a script and hides which value applied at which moment. Consecutive stubbing, or an argument-dependent answer, expresses it more honestly. - **Re-stubbing on a spy** goes through the recording mechanism and therefore invokes the real method again during the `when(...)` expression - a second real side effect. - **reset(mock)** wipes stubbings *and* recorded interactions. The documentation itself treats it as a code smell: it usually means one test is exercising two scenarios, and splitting the test removes the need. It is occasionally defensible for an expensive mock in a shared container-managed context, but it should be an explicit, commented decision. ## What good looks like Keep `@BeforeEach` stubbing to the calls every test in the class genuinely needs, with values no test disagrees with. Put scenario-specific behaviour in the test. Let strict stubs delete the leftovers for you: an `UnnecessaryStubbingException` is a free review comment saying "this setup is lying about being shared". ## The interview-sized answer "Last matching stubbing wins, because Mockito scans newest-first. The earlier one is not removed - and if it is never used, strict stubs will fail the class with UnnecessaryStubbingException, which is the right signal to move the stub into the tests that need it. Broad-in-setup plus narrow-in-test is fine, because both are used."

  • Your @BeforeEach stub is fully overridden in every test and the class now fails with UnnecessaryStubbingException. What is the best fix?
    Move the stubbing out of setup and into the tests that actually need it - the exception is telling you it is not shared behaviour. If a few tests genuinely need it as optional background, mark that one stubbing lenient() rather than loosening strictness for the whole class. Turning strictness off class-wide hides the same problem everywhere else.
  • When is reset(mock) justified, and what does it cost?
    Rarely. It clears stubbings and recorded interactions, so any verification of earlier calls is lost and the reader cannot tell what state the mock is in at a given line. It is usually a sign one test covers two scenarios and should be split. The defensible case is an expensive mock owned by a shared, container-managed context, and even then it deserves a comment explaining why.

saying these in an interview costs you the question

  • Believing the first stubbing wins, or that Mockito errors on a duplicate stubbing
  • Thinking re-stubbing appends to a consecutive thenReturn(a, b) sequence
  • Disabling strictness class-wide instead of moving or marking the offending stub
  • Using reset(mock) routinely between phases of a long test
  • Assuming a broad any() setup stub plus a specific override will always trigger UnnecessaryStubbingException - if both are used, it does not

context