In Mockito, what is the difference between chaining two answers in a single stubbing — thenReturn(1).thenReturn(2) — and writing two separate when(...) statements for the same call?
answer
- one statement = queue; two statements = override
- last stubbing registered wins
- loop-built stubs overwrite each other
- broad matcher registered later shadows narrow
- override a constant, chain a sequence
basics
~20 sChaining inside one statement builds an ordered queue: first call gets 1, second gets 2. Two separate when(...) statements re-stub the same invocation, so the later one replaces the earlier and every call returns 2. Sequences must live in one chain.
solid answer
~50 sThey are not the same thing at all. ```java when(repo.count()).thenReturn(1).thenReturn(2); // queue: 1, then 2, then 2 forever when(repo.count()).thenReturn(1); when(repo.count()).thenReturn(2); // override: 2 from the very first call ``` A chain (or the varargs form `thenReturn(1, 2)`) attaches an ordered list of answers to one stubbing. A second `when(...)` statement for the same invocation re-stubs it — the newest stubbing wins and the earlier value is never observed. The practical bug this produces is "my first value never appears", usually when someone adds a special case in a nested setup method or a `@BeforeEach` and a later per-test stubbing quietly overrides it. It is also why you cannot build a sequence in a loop by calling `when(...)` repeatedly: collect the values and pass them as varargs instead. Overriding itself is legitimate and useful — a shared `@BeforeEach` sets a sane default and one test narrows it. Just do it deliberately, and never as a way to express a sequence.
code
java · 6 lines// queue: 1, then 2, then 2 forever
when(repo.count()).thenReturn(1).thenReturn(2);
// override: every call returns 2, the 1 is never seen
when(repo.count()).thenReturn(1);
when(repo.count()).thenReturn(2);go deeper
State the core contrast: one statement queues, two statements override, so a sequence must be a single chain.
Add the loop pitfall and the varargs equivalence, and note that overriding a shared @BeforeEach default is an intentional pattern.
Bring in matcher overlap and registration order, and the diagnosis routine for a mock that returns an unexpected value including stubs inherited from base test classes.
Discuss test-suite design — when a growing pile of overrides means the collaborator should be replaced by a fake or the unit under test decomposed.
## Two mechanisms that look alike Mockito stores stubbings per *invocation matcher* — the method plus its argument matchers on a given mock. Both of the snippets below touch the same invocation matcher, `repo.count()`, but they mean different things. **Chaining** attaches an ordered queue of answers to one stubbing: ```java when(repo.count()).thenReturn(1).thenReturn(2); ``` `when(...)` returns an `OngoingStubbing`, and each `thenReturn`/`thenThrow`/`thenAnswer` on it appends another answer to that same stubbing. Calls pop answers in order and then stick on the last one: 1, 2, 2, 2, ... **Re-stubbing** creates a new stubbing that shadows the old one: ```java when(repo.count()).thenReturn(1); when(repo.count()).thenReturn(2); ``` The second statement does not append to the first. Mockito resolves an invocation against the most recently registered matching stubbing, so from the moment the second line runs, every call returns 2 — the value 1 is never observed by the code under test. ## Why this trips people up The two forms are visually close, and the failure is silent: no exception, no warning, just a test asserting on a first value that the mock never produced. The symptom is usually reported as "consecutive stubbing doesn't work". A closely related trap is loop-built stubbing: ```java for (String page : pages) { when(client.nextPage()).thenReturn(page); // each iteration overrides the previous } ``` This leaves only the final page stubbed. The correct form collects the values and hands them to the varargs overload: ```java when(client.nextPage()).thenReturn(pages.get(0), pages.subList(1, pages.size()).toArray(new String[0])); ``` or, more readably, builds one chain explicitly. ## When overriding is the right tool Overriding is a deliberate and useful feature, not just a hazard. The standard pattern is a shared setup that installs sane defaults for a collaborator so that every test does not repeat five lines of stubbing, and then an individual test that re-stubs the one call it cares about: ```java @BeforeEach void defaults() { when(featureFlags.enabled("beta")).thenReturn(false); } @Test void usesBetaPathWhenFlagOn() { when(featureFlags.enabled("beta")).thenReturn(true); // intentional override ... } ``` This reads well precisely because the override is expressing "this test differs in exactly one way". The rule of thumb: override to change a *constant*, chain to express a *sequence*. Never mix the two ideas in one test. ## Interaction with argument matchers Stubbings are keyed by arguments, so what looks like an override may not be one: ```java when(repo.find(1L)).thenReturn(a); when(repo.find(2L)).thenReturn(b); // different matcher — both stubbings coexist ``` But a broad matcher registered later shadows a narrow one for the calls it matches: ```java when(repo.find(1L)).thenReturn(a); when(repo.find(anyLong())).thenReturn(b); // now find(1L) returns b as well ``` So ordering matters when matchers overlap: register the general default first and the specific case afterwards, not the other way round. ## What the varargs form does `thenReturn(1, 2, 3)` is a single stubbing with three queued answers — exactly equivalent to chaining three `thenReturn` calls. The signature is `thenReturn(T value, T... values)`; the varargs overload only applies when you pass at least two arguments, so there is no ambiguity for the single-value case. ## Diagnosing it in a real test When a mock returns an unexpected value, three checks resolve almost every case. First, search the test class *and* its base classes and setup methods for other `when(...)` statements naming the same method — an override may live in an inherited `@BeforeEach`. Second, check for a broader matcher registered later. Third, remember that consecutive answers are consumed globally across the test, not per assertion block: if setup code or an earlier arrange step invokes the mock, it silently consumes the first answer and shifts everything by one — which is another reason to keep chains short and to verify call counts explicitly. ## Guidance Express sequences in exactly one statement, keep them to the shortest run that names a scenario, and treat any second `when(...)` on the same call as an override you must justify. If you find yourself needing many overrides for a single collaborator, that collaborator is usually doing too much and a hand-written fake will read better than the stub pile.
- You have a list of pages to return one per call. Why does stubbing inside a for-loop not work, and what do you do instead?Each loop iteration issues a fresh when(...) statement for the same invocation, so every iteration overrides the previous one and only the last page remains stubbed. Build a single stubbing instead: pass all the values to the varargs overload thenReturn(first, rest...), or write one explicit chain. The queue must belong to one stubbing statement.
- If two stubbings use overlapping argument matchers, which one wins?The most recently registered matching stubbing wins, so a broad matcher such as anyLong() declared after a specific value stubbing will shadow it for those arguments. The habit that avoids surprises is to register the general default first and the specific overrides afterwards, and to prefer distinct, non-overlapping matchers when the test can express them.
saying these in an interview costs you the question
- Expecting two separate when(...) statements to queue answers in order
- Building a sequence by stubbing inside a loop
- Assuming the earliest stubbing wins when two stubbings match the same call
- Thinking overriding is always a mistake — shared defaults plus a per-test override is a normal pattern
- Believing thenReturn(a, b) behaves differently from chained thenReturn calls