In WireMock, which of two equal-priority stubs serves a request they both match?
answer
- both mappings stay registered
- the sort uses two keys
- the second key is arrival order
- later arrival ranks ahead at equals
- newest wins only at equal priority
basics
~20 sAt equal priority WireMock serves the stub registered most recently, because it sorts matching stubs by priority ascending and then by insertion index descending. Newest-wins is only the tie-break. A later stub with a weaker priority number still loses.
solid answer
~50 sWireMock sorts the stubs that matched a request on two keys, in order: **priority ascending**, then **insertion index descending**. The first key puts the lowest `atPriority()` number at the front; the second key is consulted only for stubs the first key could not separate, and among those the most recently registered mapping wins. So "the newest stub wins" is true only at equal priority — a stub registered later at `atPriority(10)` still loses to one registered earlier at `atPriority(1)`. The reason this rule dominates ordinary suites is that most stub sets never call `atPriority()` at all: every mapping sits in the same flat band at `DEFAULT_PRIORITY`, the priority key separates nothing, and registration order silently becomes the whole rule. Registering a second stub for the same criteria does not overwrite the first — both mappings stay in the set, and the later one simply sorts ahead.
code
java · 13 linesimport static com.github.tomakehurst.wiremock.client.WireMock.*;
// neither stub calls atPriority(), so both sit at DEFAULT_PRIORITY (5)
stubFor(get(urlPathEqualTo("/falconry/v1/birds/kestrel-7/weights"))
.willReturn(aResponse().withStatus(200)));
stubFor(get(urlPathMatching("/falconry/v1/birds/[^/]+/weights"))
.willReturn(aResponse().withStatus(503)));
// GET /falconry/v1/birds/kestrel-7/weights matches both mappings.
// Equal priority, so the insertion-index tie-break decides and the
// 503 stub, registered last, is the one that answers.go deeper
Know that when two WireMock stubs match and neither calls atPriority(), the one registered later is the one that answers. Both mappings stay registered; the older one is simply never served.
Be able to state the full rule: priority ascending first, then insertion index descending. Explain why that makes newest-wins true only at equal priority, and why an unnumbered stub set is one flat band where the tie-break decides everything.
Show how this produces order-dependent suites. Explain why a test can pass alone and fail in a run, and argue for pinning the ordering with an explicit number rather than moving registration calls around.
Decide whether registration order may ever carry meaning in your suites. Weigh the cost of mandating explicit numbers everywhere against the cost of a rule that only holds while nobody appends another mapping.
## The rule has two keys, not one When more than one WireMock stub matches an incoming request, WireMock sorts the matches and serves the first. The sort has two keys, applied in this order: 1. **Priority, ascending.** The lowest number comes first. A stub that never calls `atPriority()` holds `DEFAULT_PRIORITY`, whose value is five. 2. **Insertion index, descending.** Among stubs holding the same priority number, the one added to the set most recently comes first. The second key runs only on the stubs the first key could not separate. That single sentence is what the folk rule "the newest stub wins" gets wrong, and it is the whole content of this question. ## Why "the newest stub wins" is a half-truth Put two stubs against one `GET /falconry/v1/birds/kestrel-7/weights` and work through the combinations: | stub registered first | stub registered second | which one serves the request | |---|---|---| | no `atPriority()` call | no `atPriority()` call | the second — equal priority, so arrival order decides | | `atPriority(1)` | no `atPriority()` call | the first — one outranks five, arrival order never consulted | | `atPriority(5)` | `atPriority(10)` | the first — five outranks ten | | `atPriority(10)` | `atPriority(10)` | the second — equal priority again | Rows one and four are where the tie-break actually fires. Rows two and three are the ones that break the folk rule: the later stub is newer and still loses, because the priority key had already settled the order before insertion index was looked at. ## Why the tie-break dominates ordinary suites Most stub sets never call `atPriority()` anywhere. Every mapping in them sits in the same flat band at five, the priority key separates none of them, and the tie-break becomes the only thing deciding which stub answers. That is not a rare corner case — it is the default state of almost every WireMock suite that has grown past a handful of mappings. The practical consequences: - Which stub answers depends on the order the registering code ran, which is the order your fixtures, base classes and per-test setup happen to execute in. - Moving a `stubFor` call from a shared setup method into a single test, or the reverse, can change which stub answers without changing a single matcher. - A test that passes alone can fail in a suite, or the reverse, because the set contained different mappings at the moment the request arrived. - Nothing in the mapping text records the intent. A reader cannot tell from the stub whether it was meant to outrank its neighbour or merely happened to. ## Registering the same criteria twice does not overwrite A common assumption is that a second `stubFor` for the same path replaces the first, the way assigning to a map key would. It does not. Both mappings are added to the set and both remain there; the newer one simply sorts ahead of the older one at equal priority, so the older one is still registered, still matches the request, and is never served. This is why a stub that "stopped working" is so often still present and still correct — it lost an ordering, not a match. ## What the tie-break is safe for, and what it is not - **Safe:** a deliberate, local override registered inside one test, when you can see every competing mapping and the registration order is obvious from a few lines of code. - **Safe:** a throwaway exploratory fixture where nothing else registers overlapping mappings. - **Not safe:** ordering between a shared fixture and a per-test stub, where the execution order is a property of the harness rather than of the mappings. - **Not safe:** ordering that a reader is expected to reconstruct from where the calls happen to sit in a file. - **Not safe:** anything a second team will maintain, because the next mapping someone appends inherits the front of the queue for free. ## Pinning the ordering instead of inheriting it The fix is to move the decision out of registration order and into a number, so that the mapping states its own rank. Give the stub that must answer a lower explicit `atPriority()` value than the mapping it has to beat, and the ordering stops depending on which line ran first. In the falconry suite, the kestrel-7 stub gets `atPriority(1)` and the broad weight-log stub is left on the default band or pushed to `atPriority(10)`; now the two `stubFor` calls can be reordered, split across files, or moved between setup methods without changing the answer. The interview point to land is the precise shape of the rule rather than a slogan. "Newest wins" is a correct statement about one of the two sort keys and a wrong statement about the rule as a whole, and the difference between those two is exactly the class of bug this leaf exists to prevent.
- If a stub registered later loses, what does that tell you about the two mappings?That they hold different `atPriority()` numbers and the earlier one is lower. WireMock only reaches the insertion-order key for mappings that tie on priority, so a later stub losing is proof the priority key already separated them. Compare the two numbers before you look at anything else.
- Does calling stubFor() twice with identical criteria leave one mapping or two?Two. WireMock adds the second mapping rather than replacing the first, so both stay in the set and both still match. The newer one sorts ahead at equal priority and answers every request, while the older one remains registered and silently unused.
saying these in an interview costs you the question
- Says the newest WireMock stub always wins, whatever the priority
- Thinks a second stubFor() call overwrites the first mapping
- Believes WireMock rejects two mappings with identical criteria
- Assumes the earliest registered WireMock stub wins a tie
- Relies on fixture ordering rather than an explicit atPriority number