In a MockK test you write `every { repo.find(any()) } returns A` and later, in the same test, `every { repo.find(1) } returns B`. Which stub wins, and does re-stubbing reset an existing answer chain or the mock's recorded calls?
answer
- last registered MATCHING stub wins
- recency > specificity — order the every blocks deliberately
- broad defaults early, narrow overrides late
- re-stub ⇒ new sequence, counter back to zero
- recorded calls untouched; every{} is not an interaction
basics
~20 sThe most recently registered matching stub wins, so find(1) returns B and everything else returns A. Declaration order decides, not how specific the matcher is. Re-stubbing a call installs a new answer, so any chain in the old one is abandoned and sequencing starts over — but recorded calls are untouched.
solid answer
~50 sMockK keeps the recorded stubs for a mock in registration order and, on each invocation, picks the **last registered stub whose matchers match**. So in that example `find(1)` returns `B` and `find(2)` returns `A`. Reverse the two `every` blocks and `find(1)` returns `A` — specificity is irrelevant; recency wins. That is why the idiomatic layout is broad stub first, narrow overrides after. Re-stubbing the *same* call is the degenerate case: the new answer shadows the old one completely. If the old answer was a sequence (`returns 1 andThen 2`), its position is discarded along with it and the new sequence starts at entry one. Two things re-stubbing does **not** do: it does not clear the mock's recorded calls, so verifications still see everything that happened before the re-stub; and the `every` block itself is not counted as an interaction, so recording a stub never inflates a call count. To wipe answers, use `clearMocks`, which takes independent flags for answers, recorded calls and child mocks.
code
kotlin · 13 linesval repo = mockk<Repo>()
every { repo.find(any()) } returns "default"
every { repo.find(1) } returns "special"
repo.find(1) // "special"
repo.find(2) // "default"
// reversed registration:
every { repo.find(1) } returns "special"
every { repo.find(any()) } returns "default"
repo.find(1) // "default" <- the wildcard was registered lastgo deeper
Know that stubbing the same call twice makes the later stub win, and that stubbing does not count as calling.
State the rule precisely — last registered matching stub wins, specificity is irrelevant — and know that re-stubbing restarts an answer sequence.
Separate the registers: answers versus recorded calls versus child mocks, what clearMocks does to each, and how you debug a shadowed stub in a shared fixture.
Set the convention: broad defaults in shared setup, narrow overrides in the test, no mid-test re-stubbing except for explicit phase changes, so precedence never has to be reasoned about under pressure.
## The dispatch rule in one sentence Each mock holds an ordered list of registered stubs — matcher plus answer. When a call arrives, MockK scans that list **in reverse registration order** and uses the first stub whose matchers accept the invocation. Last match wins. ## What that means in practice ### Recency beats specificity ```kotlin every { repo.find(any()) } returns A // registered first every { repo.find(1) } returns B // registered second repo.find(1) // B repo.find(2) // A ``` Swap the two lines and `find(1)` returns `A`, because the `any()` stub is now the most recent match and it accepts the argument `1`. Nothing in MockK ranks a literal matcher above a wildcard. This trips up people who expect the "most specific rule wins" behavior of routing tables or exception handlers. The practical convention that follows: in a shared `@BeforeEach` set up the broad defaults, and let individual tests add narrower overrides afterwards. That ordering works with the rule instead of against it. The inverse — a specific stub in setup and a broad `any()` stub inside one test — silently disables the setup for that test, which is one of the more annoying MockK debugging sessions. ### Re-stubbing the identical call ```kotlin every { clock.now() } returns t0 every { clock.now() } returns t1 // from here on, t1 ``` The second registration simply becomes the newest match. The first stub is still in the list but is never reached — it is shadowed, not deleted. Behaviorally that is indistinguishable from replacement, with one nuance: if you later clear only the newest registration you do not "fall back" to the old one, because clearing removes answers wholesale rather than popping the top of a stack. ### Chains reset An answer sequence is one object with an internal counter. Re-stubbing installs a *new* sequence object with a counter at zero: ```kotlin every { c.next() } returns 1 andThen 2 c.next() // 1 every { c.next() } returns 7 andThen 8 c.next() // 7, not 8 ``` This is exactly what you want for a test with two phases, and exactly what bites you if a helper function re-stubs a call you were mid-sequence on. ### Recorded calls survive Stubbing and recording are separate registers. Registering an answer never touches the invocation log, so a verification after a re-stub still counts calls made before it. If a test genuinely wants "phase two starts from zero", it must clear the recorded calls explicitly — typically `clearMocks(mock, answers = false, recordedCalls = true)`, since `clearMocks` exposes independent flags for answers, recorded calls, child mocks and verification marks. Note that clearing answers puts a non-relaxed mock back into the strict state where the next unstubbed call fails, which is often a *useful* way to prove nothing calls the collaborator after a certain point. ### The `every` block is not an interaction The lambda passed to `every` really does invoke the method, but MockK is in recording mode: the invocation is used to build the matcher and is not added to the call log. So stubbing a call five times does not make `verify(exactly = 5)` pass. Candidates occasionally believe the opposite and "explain" a failing count with it. ## Diagnosing precedence problems Symptoms of a precedence bug are distinctive: a stub you can see in the file appears to have no effect, or a value from a completely different test setup shows up. The checks, in order: 1. Look for a later `every` on the same function with a *wider* matcher — including ones hidden in helper functions, `@BeforeEach`, or a shared fixture builder. 2. Confirm the matchers really match what production passes (a stub on `find(1)` does nothing if the code calls `find(1L)` or passes a data object that is not equal). 3. Confirm nothing cleared the mock between setup and exercise. And the structural fix is usually to stop re-stubbing at all: one stub per call per test, defaults broad and early, overrides narrow and late. ## Summary - Reverse registration order; last matching stub wins; specificity does not matter. - Re-stubbing shadows the older answer and resets any sequence counter. - Recorded calls are independent of stubs and survive re-stubbing. - `every` blocks are recordings, not interactions. - `clearMocks` is the tool for actually removing answers, with per-register flags.
- A stub written inside a test seems to have no effect and an old value keeps coming back. How do you debug it?Look for a later registration on the same function with a matcher that also accepts the argument — often a wildcard stub in a shared fixture or helper that runs after your line. Then check that the matchers genuinely match the production arguments, including types and equality of value objects. Finally check nothing cleared the mock in between.
- How do you actually remove answers rather than shadow them, and what changes for the mock afterwards?Use clearMocks with its answers flag, which drops registered stubs; the flags for recorded calls, child mocks and verification marks are independent, so you can wipe one register and keep another. After answers are cleared, a non-relaxed mock is strict again, so the next unstubbed call fails — which is a useful way to assert that a collaborator is not touched in a later phase of the test.
- Does writing an every block for a call count as an interaction on the mock?No. The lambda invokes the method, but MockK is in recording mode and uses that invocation to build matchers rather than logging it as a real call. Verification counts are therefore unaffected by how many times you stub something.
Like CSS declarations with equal weight: the rule written last paints over the earlier one, no matter how narrowly the earlier selector was written.
saying these in an interview costs you the question
- Believing the most specific matcher wins regardless of declaration order.
- Assuming re-stubbing continues an existing andThen sequence rather than restarting it.
- Thinking re-stubbing clears recorded calls, and expecting verification counts to reset.
- Claiming every { } blocks count as invocations and inflate call counts.
- Treating clearMocks and unmockkAll as the same operation.