skip to content

How do you make a mocked method return different values on consecutive calls, and what happens after the listed values run out?

level: middleimportance: should knowfreq 55%

answer

  1. thenReturn(a, b, c) or .thenReturn(a).thenReturn(b)
  2. last value is sticky after the list runs out
  3. chain to mix returns and throws
  4. per stubbed call, not global
  5. thenReturn(aList) returns the whole list, not a sequence

basics

~10 s

Chain them: thenReturn(a, b, c) or thenReturn(a).thenReturn(b).thenReturn(c). The first call returns a, the second b, the third c. After the list runs out, every later call keeps returning the last value.

solid answer

~40 s

Mockito supports consecutive stubbing for methods whose result changes per call — like an iterator's next() or a poll() that empties. You either pass multiple values to one thenReturn — thenReturn(a, b, c) — or chain calls: thenReturn(a).thenReturn(b). The first matching invocation yields a, the second b, the third c. Once the listed values are exhausted, the last value is returned for all further calls (it doesn't reset or throw). You can mix outcomes in the chain: thenReturn(a).thenThrow(new IllegalStateException()) returns a first, then throws on every later call. The varargs form and the chained form are equivalent; chaining is required when you want to mix returns and throws. This is the idiomatic way to simulate stateful collaborators in a single stubbing statement.

go deeper

for a junior

Knows you can pass multiple values to thenReturn to get different results per call.

for a middle

Can choose varargs vs. chaining, knows the last value is sticky, and can mix thenReturn/thenThrow for retry/error simulation.

for a senior

Reasons about when consecutive thenReturn vs. thenAnswer is clearer, understands per-invocation tracking, and uses it to test stateful collaborators and retry/backoff paths.

for a principal

Guides when stateful stubbing signals a design smell (over-mocking a collaborator that should be a real fake) and sets conventions for readable, intention-revealing stubs.

## Why consecutive returns exist Some methods legitimately return a *different* value each time they're called: `Iterator.next()`, a queue's `poll()`, a paging API that returns page 1, page 2, then empty, a retry where the first attempt fails and the second succeeds. A single `thenReturn(value)` can't model that — it returns the same thing forever. **Consecutive stubbing** lets one stub hand back a *sequence* of values. ## Two equivalent forms **Varargs:** ```java when(iter.next()).thenReturn("a", "b", "c"); ``` **Chained:** ```java when(iter.next()).thenReturn("a").thenReturn("b").thenReturn("c"); ``` Both program the same sequence. Call results: ``` iter.next(); // "a" (1st matching call) iter.next(); // "b" (2nd) iter.next(); // "c" (3rd) iter.next(); // "c" (4th and beyond) ``` ## What happens when values run out The **last** value is *sticky*: after the listed values are used up, every subsequent call returns that last value. The sequence does **not** wrap around to the start, does **not** revert to a default, and does **not** throw. If you actually want "throw once we're past the data," make the last step an exception (below). ## Mixing returns and throws The chained form can mix outcomes — this is the one thing varargs `thenReturn(...)` can't do: ```java when(service.call()) .thenReturn("ok") // 1st call .thenThrow(new TimeoutException()); // 2nd and onward throw ``` A classic use is testing retry logic: fail first, succeed second: ```java when(service.call()) .thenThrow(new IOException()) // 1st: simulate failure .thenReturn("ok"); // 2nd: simulate recovery ``` ## Per-argument independence The sequence is tracked **per stubbed invocation pattern**, not globally across the mock. `when(svc.get(1)).thenReturn(a, b)` and `when(svc.get(2)).thenReturn(c, d)` advance independently — calling `get(1)` twice then `get(2)` gives `a, b, c`. ## thenReturn vs. thenAnswer for sequences If the sequence is mechanical (e.g. count up), `thenAnswer` with a counter can be cleaner, but for a fixed, short list of known values, consecutive `thenReturn` is clearer and the standard idiom. ## Pitfall: list vs. one List value `thenReturn(list)` where `list` is a `List` returns that *whole list* once (and on every call). `thenReturn(a, b)` returns `a` then `b`. Don't confuse "a single value that happens to be a collection" with "a sequence of values." ## Mental model Think of a vending machine loaded with a row of snacks. Each press dispenses the next snack in the row; once the row is empty, the machine just keeps dispensing whatever was last in the slot.

  • How would you stub a call to fail on the first attempt and succeed on the second to test retry logic?
    Chain a throw then a return: when(svc.call()).thenThrow(new IOException()).thenReturn("ok"). The first invocation throws (simulating a transient failure), the second returns the success value, letting you assert the retry recovers.
  • If you call thenReturn(a, b) but the method is invoked five times, what are the five results?
    a, b, b, b, b — the first two use the listed values, then the last value (b) sticks for every further call.

saying these in an interview costs you the question

  • Believing the sequence loops back to the first value after exhaustion — it sticks on the last value.
  • Expecting an exception once the values run out — it doesn't throw unless you made the last step a throw.
  • Trying to mix returns and throws in a single varargs thenReturn — you must chain (.thenThrow).
  • Confusing thenReturn(myList) (one List value) with thenReturn(a, b) (a sequence).

context