skip to content

What does when(mock.method()).thenReturn(value) do in Mockito, and what happens if you call the stubbed method again or call a method you never stubbed?

level: juniorimportance: must knowfreq 80%

answer

  1. when(call).thenReturn(value)
  2. unstubbed → null/0/false, empty collections
  3. matches the exact arguments you wrote
  4. same value on every call
  5. last stubbing of a call wins

basics

~20 s

It tells a fake object: 'when this method is called, give back this value.' Call it again and you get the same value. Methods you never set up return harmless defaults like null, 0, or false.

solid answer

~40 s

when(mock.method()).thenReturn(value) is Mockito's core stubbing syntax: it programs a mock so that whenever method() is invoked with matching arguments, it returns value instead of running real code. The same value is returned on every subsequent matching call. Methods you never stub return Mockito's defaults from RETURNS_DEFAULTS: null for objects, 0/false for primitives, and empty collections for List/Map/etc., so the mock never throws just for being called. Stubbing is argument-specific: when(svc.find(1)).thenReturn(a) only fires for find(1); find(2) falls through to defaults. Re-stubbing the same call overwrites the previous behaviour. This lets a unit test isolate the class under test by feeding its collaborators canned answers, so the test exercises only your logic, not the real dependency.

go deeper

for a junior

Can write a basic when().thenReturn() and knows unstubbed methods return null/0/empty rather than throwing.

for a middle

Understands argument specificity, that the value repeats on every call, the all-or-nothing matcher rule, and that re-stubbing overwrites.

for a senior

Articulates how when() actually works (intercepted invocation, not a real call), distinguishes stubbing from verification, and reasons about when defaults hide bugs.

for a principal

Frames stubbing within an isolation/test-design strategy, weighs mocks vs. real fakes, and sets team conventions (e.g. strict stubs, avoiding over-mocking).

## The problem stubbing solves A **unit test** checks one class (the *system under test*, SUT) in isolation. But that class usually talks to *collaborators* — a repository, an HTTP client, a clock. If the test used the real collaborators it would be slow, flaky, and would test them too. A **mock** is a fake stand-in for a collaborator: an object that records how it was called and returns whatever you tell it to. **Mockito** is the most popular Java mocking library. **Stubbing** means *programming the mock's answers* — "when somebody asks you X, reply Y." ## The syntax ```java List<String> mock = mock(List.class); when(mock.get(0)).thenReturn("hello"); ``` Read it as: *when* `mock.get(0)` *is called, then return* `"hello"`. Now `mock.get(0)` evaluates to `"hello"` every time. Mechanically, `when(...)` does **not** really call the method to get a value. The call `mock.get(0)` runs first (it's ordinary Java), but on a mock that call is *intercepted*: Mockito records "the last invocation was get(0)" and returns a dummy. `when(dummy)` then attaches the `.thenReturn(...)` answer to that recorded invocation. This is why `when()` takes the *result* of calling the method, not the method itself. ## What every call returns - **A stubbed call** returns the value you set, **on every matching call** (not just once). - **An unstubbed call** returns a *default* from Mockito's `RETURNS_DEFAULTS` answer: - object reference → `null` - `int`/`long`/`double` → `0` - `boolean` → `false` - `List`, `Set`, `Map`, `Stream`, `Optional` → **empty**, never null (a deliberate convenience) So an unstubbed method never throws — it quietly returns a benign default. ## Argument specificity A stub matches only the arguments you wrote: ```java when(repo.findById(1L)).thenReturn(user); repo.findById(1L); // -> user repo.findById(2L); // -> null (unstubbed, default) ``` To match any argument, use **argument matchers**: `when(repo.findById(anyLong())).thenReturn(user)`. Rule: if you use one matcher in a call, *all* arguments must be matchers. ## Overwriting and order Re-stubbing the same call replaces the old answer — the **last** stubbing wins: ```java when(mock.get(0)).thenReturn("a"); when(mock.get(0)).thenReturn("b"); // now returns "b" ``` ## Stub vs. verify Stubbing controls *inputs to the SUT* (what the collaborator gives back). **Verification** (`verify(mock).method()`) checks *outputs from the SUT* (that it called the collaborator). They are different concerns; a value-returning method you depend on is usually stubbed, not verified. ## Mental model A mock is a programmable answering machine. By default every button plays "empty." `when().thenReturn()` records a custom message for one specific button (one method + argument set). Pressing it again replays the same message; buttons you never recorded play the default.

  • Why does an unstubbed method that returns a List give back an empty list instead of null?
    Mockito's default RETURNS_DEFAULTS answer returns empty collections (and Optional/Stream) instead of null as a convenience, so callers iterating the result don't NPE. You can change this with a different default Answer like RETURNS_SMART_NULLS.
  • How do you make a stub match any argument value?
    Use argument matchers, e.g. when(repo.findById(anyLong())).thenReturn(user). If any argument uses a matcher, all of them must — mixing a raw value with a matcher throws InvalidUseOfMatchersException; wrap the raw value in eq(...).

saying these in an interview costs you the question

  • Thinking thenReturn only returns once then stops — it returns the same value on every matching call.
  • Expecting an unstubbed method to throw — it returns a default (null/0/empty), which silently hides bugs.
  • Believing when() literally calls the real method to capture a value — on a mock the call is intercepted.
  • Assuming a stub for find(1) also answers find(2) — stubs are argument-specific.

context