In MockK, what exactly does a verifyOrder block check about the calls a mock recorded — which calls are allowed to appear between the ones you list, how do you assert that the same call happened twice in a row, and what makes the block fail?
answer
- subsequence, not substring
- gaps allowed, unlisted calls ignored
- one recorded call satisfies one entry
- order spans multiple mocks
- no exactly/atLeast — only inverse
basics
~20 sverifyOrder asks whether the listed calls appear in that relative order somewhere in MockK's chronological call record. Any other calls in between are ignored. A repeat must be listed once per occurrence. It fails only when no in-order match exists.
solid answer
~50 sMockK records every invocation on a mock into a chronological log. `verifyOrder` runs a **subsequence** query over that log: each listed call must match a recorded call, and the matched positions must increase. Anything in between — other calls on the same mock, calls on mocks not mentioned in the block — is ignored, so it is the *relative* order that is asserted, not a complete protocol. A repeated call is not implied by listing it once: one recorded call can satisfy only one entry, so `verifyOrder { m.f(); m.f() }` requires two matching invocations. Order is tracked across mocks too, so you can list `a.x()` then `b.y()` and MockK checks the real chronology. It fails when a listed call never happened, when the matchers do not match, or when the only matching calls are out of order. `verifyOrder` takes no count arguments — only `inverse`; it is shorthand for `verify(ordering = Ordering.ORDERED) { ... }`. Suspend equivalent: `coVerifyOrder`.
code
kotlin · 9 linesval transport = mockk<Transport>(relaxUnitFun = true)
service.publish(batch) // send(), log(), send(), flush()
verifyOrder {
transport.send(any())
transport.send(any())
transport.flush()
}go deeper
Say clearly that verifyOrder checks relative order and tolerates other calls in between, and show one block that lists two calls on the same mock.
Describe the subsequence semantics precisely — increasing positions, gaps ignored, one record per listed entry — and note that counts must be asserted separately.
Add the cross-mock chronology, the Ordering.ORDERED primitive behind the shorthand, and the diagnostic habit of suspecting matchers first when an order block fails.
Frame it as a choice about which orderings are contractual: verifyOrder pins causal relationships while leaving room for chatty collaborators, which is what keeps interaction tests survivable across refactors.
## The call record is the thing being queried Every time production code invokes a method or property on a MockK mock, MockK appends that invocation to a chronological record: which mock, which member, which argument values, in what order. Stubbing (`every { ... }`) writes to a *different* structure — the answer table. All the verification functions (`verify`, `verifyOrder`, `verifySequence`, `verifyAll`) are simply different queries over the call record, and they differ only in what makes the query succeed. ## What verifyOrder asks `verifyOrder` asks: *can I find, for each call I listed, a recorded call that matches it, such that the matched positions are strictly increasing?* That is a subsequence match, not a substring match. Concretely, with recorded calls `f(), log(), g(), h()`: - `verifyOrder { m.f(); m.g() }` passes — positions 0 and 2 increase; `log()` in the gap is irrelevant. - `verifyOrder { m.g(); m.f() }` fails — no increasing pair matches. - `verifyOrder { m.f(); m.h() }` passes even though two calls sit between them. So `verifyOrder` constrains only the calls you name, and only relative to each other. Calls you did not name are invisible to it. That is exactly why it is the least brittle of the ordering modes: adding a new interaction to production code does not break it unless the new interaction changes the relative order of the calls that were named. ## Repeats are positions, not counts A single recorded call can satisfy at most one entry in the block. If the contract is "`send` was called, then `send` again, then `flush`", you write: ```kotlin verifyOrder { transport.send(any()) transport.send(any()) transport.flush() } ``` Listing `send` once would pass even if it happened five times, because the query is satisfiable with one match. And note what is *not* available: `verifyOrder` has no `exactly`, `atLeast` or `atMost` parameters — its only parameter besides the block is `inverse`. Counting and ordering are separate tools in MockK; if you need both, verify the count in a separate unordered `verify(exactly = n) { ... }` and the order in a `verifyOrder` block. ## Ordering spans mocks Because the record is chronological across the whole test, a single `verifyOrder` block may reference several mocks: ```kotlin verifyOrder { tx.begin() repo.save(any()) tx.commit() } ``` This is where ordering verification usually earns its keep: it pins a *causal* relationship between collaborators (write inside the transaction, not after commit) that no return-value assertion can express. ## The primitive underneath `verifyOrder(...)` is shorthand for `verify(ordering = Ordering.ORDERED) { ... }`. MockK's `Ordering` enum has four values — `UNORDERED` (plain `verify`), `ALL` (`verifyAll`), `ORDERED` (`verifyOrder`) and `SEQUENCE` (`verifySequence`) — and the shorthand functions just pass one of them. Knowing that makes the family easy to reason about: one query engine, four success criteria. Suspend functions use the `co` variants (`coVerifyOrder`), which behave identically but can call suspend members inside the block. ## Failure modes A `verifyOrder` block fails when: 1. A listed call never happened at all (wrong member, or the arguments do not satisfy the matchers you wrote). 2. Matching calls exist but only in the wrong order. 3. You listed a call *n* times and fewer than *n* matching invocations were recorded. The failure is an `AssertionError` whose message contains the verification block you wrote and the calls MockK actually recorded, so the first diagnostic step is always to read the recorded-call list: most "order" failures are really matcher failures — an argument did not match, so the call was never a candidate in the first place. ## Practical guidance Use `verifyOrder` when order is part of the contract and the collaborator is allowed to be chatty. Reach for it in preference to `verifySequence` unless you genuinely intend to freeze the complete interaction, because the tolerance for gaps is what keeps the test alive across refactors that add logging, metrics or an extra read.
- How would you express "exactly three sends, and all of them before flush" with MockK?Split the assertion. Use `verify(exactly = 3) { transport.send(any()) }` for the count, and a separate `verifyOrder { transport.send(any()); transport.send(any()); transport.send(any()); transport.flush() }` for the ordering. `verifyOrder` accepts no count parameters, so the count has to come from an unordered `verify`. The order block pins three distinct sends before the flush; the count block rules out a fourth.
- A verifyOrder block fails but the log shows the calls happened in the order you listed. What is the usual cause?Argument matching, not ordering. If a listed call uses `eq(...)` semantics on a value that differs — a different data-class instance, a mutated argument captured by reference, a nullable that arrived null — that entry matches no recorded call, so no valid in-order match exists and MockK reports an ordering failure. Read the recorded-call list in the error message and compare arguments before suspecting order.
It is like checking a flight itinerary for "London before Tokyo before Sydney": stopovers anywhere in between are fine, but the three cities must appear in that order, and "London twice" means two separate stamps.
saying these in an interview costs you the question
- Believing verifyOrder fails if any unlisted call happens in between — that is verifySequence's job
- Assuming listing a call once asserts it happened exactly once
- Thinking verifyOrder only works within a single mock and cannot express cross-collaborator order
- Trying to pass exactly/atLeast to verifyOrder and concluding MockK cannot count
- Reading an ordering failure as an ordering bug without checking that the argument matchers matched at all