skip to content

Why does Mockito have the doReturn(...).when(mock).method() form in addition to when(mock.method()).thenReturn(...), and when must you use it?

level: seniorimportance: must knowfreq 50%

answer

  1. when(mock.foo()) evaluates foo() first
  2. spies run the REAL method during when() → use doReturn
  3. void methods can't go inside when() → do* only
  4. do family: doReturn/doThrow/doAnswer/doNothing/doCallRealMethod
  5. doReturn loses compile-time return-type checking

basics

~20 s

when(mock.method()) actually calls the method first to set up the stub, which is a problem for spies (real objects) and impossible for void methods. doReturn(...).when(mock).method() avoids calling the real method, so use it for spies and void/throwing cases.

solid answer

~50 s

The when(mock.method()).thenReturn(v) form has to evaluate mock.method() to capture the invocation. On a pure mock that's harmless. But on a spy — a partial mock wrapping a real object — that evaluation runs the real method, which may have side effects or throw before you ever stub it. It also can't be used to stub void methods, because a void call can't be the argument to when(). The doReturn(v).when(spy).method() form flips the order: when() is given the mock and the stubbed method is invoked only to record it, so the real implementation is never executed during stubbing. Use the do* family — doReturn, doThrow, doAnswer, doNothing, doCallRealMethod — for: spies, void methods, and cases where the standard form would call dangerous real code. The trade-off is that doReturn is not type-checked against the method's return type at compile time, so the safer when().thenReturn() stays the default for plain mocks.

code

java · 12 lines
java
import static org.mockito.Mockito.*;

// Spy: when().thenReturn() runs the REAL method during setup
List<String> spy = spy(new ArrayList<>());
// when(spy.get(0)).thenReturn("x"); // throws IndexOutOfBoundsException at setup!
doReturn("x").when(spy).get(0);       // safe: real get(0) never runs
assertEquals("x", spy.get(0));

// void method: cannot use when(); must use doThrow/doNothing/doAnswer
List<String> mock = mock(List.class);
doThrow(new IllegalStateException()).when(mock).clear();
assertThrows(IllegalStateException.class, mock::clear);

go deeper

for a junior

Knows there are two syntaxes and that the do-form is needed for void methods.

for a middle

Can correctly stub spies and void methods with doReturn/doThrow/doNothing and explain why when() would run the real method.

for a senior

Explains the evaluation-order mechanism, the lost compile-time type safety of doReturn, and chooses the right form per situation as a default convention.

for a principal

Sets testing standards (prefer when().thenReturn() for mocks, reserve do*/spies for legacy/partial-mock cases), and steers teams away from spies toward real fakes where appropriate.

## Two ways to say the same thing Mockito offers two stubbing syntaxes: ```java when(mock.foo()).thenReturn(1); // the 'when/then' form doReturn(1).when(mock).foo(); // the 'do/when' form ``` For an ordinary mock and a value-returning method they behave the same. The difference is *how* each one captures the invocation, and that difference matters in three situations. ## How when(mock.foo()) works — and why it can bite `when(mock.foo())` is just Java: `mock.foo()` is **evaluated first**, then its result is passed to `when()`. On a mock, `foo()` is intercepted and returns a dummy, so no real code runs. Fine. Now consider a **spy**. `spy(realObject)` creates a *partial mock*: unstubbed methods run the **real** implementation. So `when(spy.foo())` actually **calls the real `foo()`** before you've stubbed it. If `foo()` hits a database, throws on a half-built object, or has side effects, your stub setup breaks — and it ran the very code you were trying to replace. ```java List<String> spy = spy(new ArrayList<>()); when(spy.get(0)).thenReturn("x"); // BOOM: real get(0) runs on empty list -> IndexOutOfBoundsException doReturn("x").when(spy).get(0); // safe: real get(0) is never called ``` ## How doReturn(v).when(spy).foo() avoids it In the `do*` form, `when(spy)` returns a stubbing proxy, and the `.foo()` that follows is the **only** invocation — and it's intercepted purely to record "stub foo() with this answer." The real `foo()` is **never executed during stubbing**. The order is reversed: you state the answer first, then attach it to the method. ## The three reasons to use do* 1. **Spies** — to stub a method without first running its real version (the example above). 2. **void methods** — you literally cannot write `when(mock.voidMethod())` because a `void` expression can't be an argument. So `doThrow`, `doNothing`, `doAnswer`, `doCallRealMethod` are the *only* way to stub them: ```java doThrow(new IllegalStateException()).when(mock).clear(); doNothing().when(mock).clear(); // (default for void, but explicit when overriding a prior doThrow) ``` 3. **Re-stubbing / dangerous real calls** — anytime evaluating the method in `when(...)` would itself misbehave. ## The do* family - `doReturn(value)` — return a value. - `doThrow(Throwable / Class)` — throw. - `doAnswer(Answer)` — dynamic answer (the void/spy counterpart of `thenAnswer`). - `doNothing()` — explicitly no-op a void method (e.g. after a previous `doThrow`). - `doCallRealMethod()` — run the real implementation (useful on mocks/partial setups). All end with `.when(mock).method(args)`. ## The trade-off: lost compile-time type safety `when(mock.getInt()).thenReturn("oops")` won't compile — `thenReturn` is generically typed to the method's return type. But `doReturn("oops").when(mock).getInt()` **compiles** and fails at runtime, because `doReturn` takes a raw `Object`. So the `when().thenReturn()` form is the **default** for plain mocks (safer, more readable); reach for `do*` only when you *must* (spies, void, dangerous reals). ## Mental model `when(mock.foo())` says "do this thing, then I'll tell you how to fake it" — but on a spy the "do this thing" really happens. `doReturn(v).when(mock).foo()` says "here's the fake answer; now attach it to foo" — foo is only named, never truly run. For real objects (spies) and for methods that return nothing (void), naming-without-running is the only safe option.

  • How do you stub a void method to throw an exception, and why can't you use when().thenThrow()?
    Use doThrow(new SomeException()).when(mock).voidMethod(). You can't write when(mock.voidMethod()).thenThrow(...) because a void method call has no value and so can't be passed as an argument to when(); the do* family is the only way to stub void methods.
  • On a spy of an ArrayList, why does when(spy.get(0)).thenReturn("x") throw, but doReturn("x").when(spy).get(0) doesn't?
    The when() form evaluates spy.get(0) before stubbing, and on a spy that runs the real get(0) on an empty list, throwing IndexOutOfBoundsException. The doReturn form never invokes the real get(0) during setup, so it stubs safely.

saying these in an interview costs you the question

  • Using when().thenReturn() on a spy and being surprised the real method runs/throws during setup.
  • Trying to stub a void method with when(mock.voidMethod()) — it can't compile.
  • Thinking doReturn is type-safe like thenReturn — it takes a raw Object and can mismatch at runtime.
  • Using doReturn everywhere by default instead of only for spies/void/dangerous cases.

context