In Mockito, how do you make a stubbed method return a different value on each successive call, and what does the mock return once the test calls that method more times than you stubbed for?
answer
- chain thenReturn = queue of answers
- thenReturn(a, b, c) varargs shorthand
- last answer sticks forever
- thenThrow can end the chain
- second when() replaces, never appends
basics
~20 sChain the answers: when(m.next()).thenReturn(1).thenReturn(2), or use the varargs shorthand thenReturn(1, 2). Calls consume answers in order. Once they run out, the mock keeps repeating the last answer forever — it does not reset, return null, or fail.
solid answer
~50 sMockito lets you queue answers for one stubbed call. Two equivalent forms: ```java when(cursor.next()).thenReturn("a").thenReturn("b"); when(cursor.next()).thenReturn("a", "b"); ``` The first invocation gets `"a"`, the second `"b"`. The important semantic is **last-answer persistence**: every invocation after the queue is exhausted repeats the final answer, so the third, fourth and hundredth call all return `"b"`. Mockito never rewinds the sequence and never throws just because you called more times than you stubbed. You can mix outcome kinds in one chain — `thenReturn("a").thenThrow(new IllegalStateException())` returns once then throws on every later call, because the throw is now the sticky last answer. The same queueing exists in the do-family form (`doReturn(1).doThrow(...).when(mock).next()`), which is what you must use when the method returns void. One trap: writing two separate `when(...)` statements for the same call does **not** append answers — the second statement replaces the first.
go deeper
Recall the two syntaxes and state plainly that the last answer repeats forever once the queue is exhausted.
Add that outcomes can be mixed (thenReturn then thenThrow), that a checked exception must be declared by the method, and that re-stubbing overrides instead of appending.
Point out that the sequence never fails on extra calls, so call count must be asserted separately with verify(times(n)), and connect the do-family twin for void methods.
Frame long sequences as a coupling smell — the test encodes an exact interaction count — and say when a stateful fake is the better collaborator.
## What consecutive stubbing is for A plain Mockito stub is constant: `when(clock.now()).thenReturn(t)` makes *every* call return the same `t`. Plenty of real collaborators are not constant — an iterator or cursor yields a new element each time, a poller returns `PENDING`, `PENDING`, `DONE`, a flaky client fails then recovers, an ID generator hands out fresh IDs. Consecutive stubbing lets one stubbing statement describe that sequence. ## The two syntaxes Chained calls on the returned `OngoingStubbing`: ```java when(cursor.next()).thenReturn("a").thenReturn("b").thenReturn("c"); ``` and the varargs shorthand, which compiles to exactly the same thing: ```java when(cursor.next()).thenReturn("a", "b", "c"); ``` The signature is `thenReturn(T value, T... values)` — a single-argument overload plus a varargs one — so the shorthand only kicks in when you pass two or more values. Both forms build an ordered queue of *answers* attached to that stubbing. ## Last-answer persistence — the part interviewers probe When the stubbed method is invoked, Mockito pops the next answer from the queue. When only one answer is left, it stops popping and keeps returning that one. So with three answers stubbed: | call | 1 | 2 | 3 | 4 | 5 | |---|---|---|---|---|---| | result | a | b | c | c | c | This is a deliberate design choice, and it is the single most misremembered fact about the feature. Candidates commonly guess that the fourth call returns `null`, that Mockito throws "no more answers", or that the sequence wraps around to the beginning. None of that happens. The practical consequence is that a test can silently pass even if production code calls the collaborator far more often than intended — the sequence never complains, so if call count matters you must assert it with `verify(cursor, times(3)).next()`. ## Mixing answer kinds in one chain The chain is not restricted to values. You can interleave outcomes: ```java when(client.fetch()).thenReturn("ok").thenThrow(new TimeoutException()); ``` First call returns `"ok"`; every call after that throws, because the throw became the sticky last answer. Reverse it to model recovery: ```java when(client.fetch()).thenThrow(new TimeoutException()).thenReturn("ok"); ``` A checked exception must be declared by the stubbed method, or Mockito rejects the stubbing immediately with a `MockitoException` about an invalid checked exception. That check happens at stub time, not at call time, so the failure points at the stubbing line. ## The do-family equivalent Every consecutive form has a do-family twin, built by chaining on the `Stubber`: ```java doReturn("a").doThrow(new IllegalStateException()).when(cursor).next(); ``` Same queue, same last-answer persistence. This matters because methods that return `void` cannot be written in the `when(mock.call())` form at all, so any sequence of behaviours for a void method must be expressed as `doThrow(...).doNothing().when(mock).send(msg)`. ## Re-stubbing is not appending A frequent source of "my second value never shows up": ```java when(cursor.next()).thenReturn("a"); when(cursor.next()).thenReturn("b"); // replaces, does not queue ``` The second statement re-stubs the same invocation, so from then on every call returns `"b"` — you never see `"a"`. Sequences must live in one chain (or one varargs call). The same applies inside loops: building stubs in a `for` loop overwrites rather than accumulates unless you collect the values and pass them as varargs. ## Matchers and sequences The queue belongs to a *stubbing*, which is identified by the method plus its argument matchers. `when(repo.find(1)).thenReturn(a, b)` sequences only calls with argument `1`; a call with `2` falls through to the default answer (`null` for objects, `0` for numbers, empty collections for collection types). If you want one sequence regardless of arguments, stub with `any()`. ## Practical guidance Keep sequences short and obviously intentional — two or three entries that model a named scenario such as "fails once, then succeeds". Long sequences bake exact call counts into the test and break whenever the production code adds a legitimate extra read. When you need more than a handful of steps, the collaborator is usually better replaced by a small hand-written fake that holds real state.
- If the chain ends with thenThrow, does the mock ever go back to returning a value?No. The final entry in the chain becomes the sticky answer, so once the exception entry is reached every subsequent invocation throws a new instance-of-the-same throwable you supplied. If you want failure-then-recovery you must order it the other way: thenThrow(...) first, thenReturn(...) last. There is no wrap-around and no automatic reset between invocations.
- Does consecutive stubbing also work with the do-family form?Yes. Stubber chains the same way: doReturn("a").doThrow(new IllegalStateException()).when(cursor).next(). The queue semantics, including last-answer persistence, are identical. You need this form for void methods, where doThrow(...).doNothing().when(mock).send(msg) is the only way to express a sequence, and for spies, where the when(mock.call()) form would execute the real method.
- How do you make sure the test notices if production code calls the method more times than the sequence covers?Add an explicit verification such as verify(cursor, times(3)).next() or verifyNoMoreInteractions(cursor). Because the last stubbed answer repeats indefinitely, extra calls are invisible to the stubbing itself, so an unbounded retry loop or an accidental double read would still make the test pass. Verification is what turns the sequence into a real assertion about call count.
saying these in an interview costs you the question
- Claiming the mock returns null, or throws, once the stubbed answers run out
- Believing the sequence rewinds and replays from the first answer
- Thinking two separate when(...) statements for the same call queue up answers
- Assuming reset(mock) is needed between calls to advance the sequence
- Saying thenReturn(a, b) and .thenReturn(a).thenReturn(b) behave differently