In Mockito, doReturn(x).when(mock).getValue() and when(mock.getValue()).thenReturn(x) stub the same call. What do you give up by choosing the doReturn form, and when is that trade worth making?
answer
- thenReturn typed via OngoingStubbing<T>
- doReturn(Object) — no compile check
- mismatch → WrongTypeOfReturnValue at runtime
- required for spies, void, generics, doCallRealMethod
- default to when(); do-family signals a special case
basics
~20 sdoReturn takes a plain Object, so the compiler cannot check that the value matches the method's return type — a mismatch only surfaces at runtime as WrongTypeOfReturnValue. It is worth it for spies, void methods, and generics cases where thenReturn will not compile.
solid answer
~50 s`thenReturn` is typed: `when(mock.getValue())` produces an `OngoingStubbing<T>` bound to the method's return type, so `thenReturn("x")` on an `int`-returning method is a **compile error**, and renaming or retyping the method breaks the test at build time — where you want it. `doReturn(Object)` is untyped by design, because the value has to be supplied before the method is known. A mismatch compiles happily and blows up at runtime with `WrongTypeOfReturnValue`, and a refactor that changes the return type silently leaves a wrong stub behind until the test executes. Worth paying when: - stubbing a **spy**, where `when(spy.call())` would run the real method; - the method returns **void**, where `when(...)` is not legal Java at all; - **generics** defeat `thenReturn` — deep wildcards or a `<T> T` signature where the inferred type will not accept your value; - using `doCallRealMethod` or partial mocking. Otherwise default to `when(...).thenReturn(...)` and keep the compiler on your side.
code
java · 8 lines// compile error: String is not Integer
// when(mock.getValue()).thenReturn("x");
// compiles, fails at runtime with WrongTypeOfReturnValue
doReturn("x").when(mock).getValue();
// generics: method returns List<? extends Number>
doReturn(List.of(1, 2)).when(mock).numbers();go deeper
Know that doReturn takes an Object so the compiler cannot check the value, and that when(...).thenReturn(...) is the typed default.
Name WrongTypeOfReturnValue and list the situations that force the do-family: spies, void methods, generics, doCallRealMethod.
Discuss refactor safety — a return-type change breaks thenReturn stubs at build time but can leave doReturn stubs stale — and set a per-collaborator convention so the syntax carries information.
Frame it as a policy call for the suite: default to the checked form, reserve the untyped one for cases that require it, and treat a codebase drowning in doReturn as a signal of spy-heavy tests and mixed responsibilities.
## Where the type safety comes from ```java when(mock.getValue()).thenReturn(42); ``` `Mockito.when` is declared `static <T> OngoingStubbing<T> when(T methodCall)`. The compiler infers `T` from the return type of `getValue()`, so the returned `OngoingStubbing<Integer>` exposes `thenReturn(Integer)`. Handing it a `String` does not compile. That is a genuine, free safety net: if someone changes `getValue()` to return a `UUID`, every stub written this way fails the build immediately with a clear message. ```java doReturn(42).when(mock).getValue(); ``` `Mockito.doReturn` is declared `static Stubber doReturn(Object toBeReturned)`. It has to be `Object`, because at the moment you call it the method being stubbed has not been named yet — it appears two calls later. So there is nothing for the compiler to check against. ## When the mismatch surfaces Mockito validates the answer against the invocation when the stubbing is completed, throwing `WrongTypeOfReturnValue` with a message naming both types ("String cannot be returned by getValue(); getValue() should return Integer"). So it does fail fast at runtime — on the stubbing line, not deep inside the code under test — which softens the loss, but it is still a test-run failure rather than a compile failure, and it only fires for tests that actually execute that line. The more insidious case is a *silent* survivor: when the new and old return types are compatible enough (widening, a supertype, an unchecked generic), a stale stub can keep compiling and running while no longer meaning what it did. ## Where doReturn is not a choice but a necessity **Spies.** `when(spy.loadAll())` evaluates `spy.loadAll()` first, and an unstubbed call on a spy runs the real method — real database hits, real exceptions, extra invocations counted by `verify`. `doReturn(list).when(spy).loadAll()` records the invocation without executing it. This alone justifies the family's existence. **Void methods.** A void call cannot be an argument in Java, so `when(mock.delete(id))` does not compile. `doNothing()`, `doThrow(...)` and `doAnswer(...)` are the only route. **Generics the compiler cannot reconcile.** Two recurring shapes: ```java // 1. method returns List<? extends Number>: thenReturn cannot accept List<Integer> cleanly doReturn(List.of(1, 2)).when(mock).numbers(); // 2. a <T> T signature where inference picks Object at the call site doReturn(customer).when(mapper).convert(row, Customer.class); ``` In both cases `thenReturn` forces an ugly cast or an `@SuppressWarnings`, and the do-family reads better precisely because it does no checking. **Partial mocking.** `doCallRealMethod().when(mock).template()` has no `then...` equivalent that avoids invoking the method. **Consecutive answers on void methods.** `doThrow(...).doNothing().when(mock).send(msg)` — the only way to express a sequence for a void call. ## Reading the two forms The `when(...).thenReturn(...)` order matches how people describe behaviour ("when this is called, return that"), which is why it is the documented default. The do-family reverses it ("return this, when this is called"), which reads less naturally and puts the interesting part — the method being stubbed — at the end of the line. That readability difference is a real, if minor, argument for keeping `when` as the default rather than standardising on the do-family for uniformity. ## A practical policy - Default to `when(mock.call()).thenReturn(value)` for plain mocks with value-returning methods. You get compile-time checking and the natural reading order for free. - Switch to the do-family whenever the target is a spy, the method is void, generics fight you, or you need `doCallRealMethod`. - Do not mix the two arbitrarily inside one test class; pick the required form per collaborator kind so a reader can infer the reason from the syntax alone. Seeing `doReturn` should signal "this is a spy or a special case", which is useful information. - When you must use `doReturn`, keep the stubbed value strongly typed in a local variable (`List<Order> orders = ...; doReturn(orders).when(spy).loadAll();`) so at least the value's own type is stated near the stub. ## Summary The do-family trades compile-time checking of the stubbed value for control over *when* the target method is invoked. On a plain mock there is nothing to control, so the trade is a pure loss; on a spy, a void method or an awkward generic signature, the control is the only thing that makes the stubbing possible at all.
- If doReturn mismatches the return type, when exactly does the test fail?At runtime, when Mockito completes the stubbing — it validates the supplied answer against the recorded invocation and throws WrongTypeOfReturnValue naming both the value's type and the method's declared return type. So the failure points at the stubbing line rather than at the code under test, but it is still a test-execution failure, not a build failure, and only for tests that reach that line.
- Are there cases where doReturn compiles and runs but is still wrong?Yes, when the types are compatible enough to pass validation but no longer mean the same thing — for example a method whose return type widened to a supertype, or an unchecked generic where erasure hides the mismatch. The stub keeps working while representing stale intent, which is exactly the class of bug that the typed thenReturn form would have caught at compile time.
saying these in an interview costs you the question
- Claiming doReturn and thenReturn are fully interchangeable with no downside
- Standardising on doReturn everywhere 'for consistency' without acknowledging the lost compile-time check
- Believing a type mismatch in doReturn is silently ignored at runtime
- Thinking the do-family exists only for void methods
- Using thenReturn on a spy because it is 'the typed one', ignoring that the real method executes