In MockRestServiceServer, how do you control expected call counts and request ordering, and test retries or multiple calls?
answer
- ExpectedCount: once/times/min/max/between/manyTimes/never
- expect(count, matcher) overload
- Ordered by default — request N matches expectation N
- ignoreExpectOrder(true) → unordered manager
- Retry test: times(3) absorbs 3 calls; or two once() responders
basics
~20 sUse ExpectedCount in server.expect(...) — like once(), times(n), min(n), max(n), manyTimes(), never() — to say how many times a request should occur. By default expectations must happen in the order recorded; you can build an unordered server (ignoreExpectOrder) so requests match in any order, which is useful for retries or concurrent calls.
solid answer
~40 s`server.expect(...)` takes an optional `ExpectedCount` as its first argument: `expect(ExpectedCount.times(3), requestTo(url))`, plus `once()`, `manyTimes()`, `min(n)`, `max(n)`, `between(a,b)`, `never()`. This controls how many matching requests the single expectation absorbs — ideal for testing a retry policy that hits the same URL N times. Ordering: by default `MockRestServiceServer` enforces **strict recorded order** — request 1 must match expectation 1. When calls can arrive in any order (retries with jitter, parallel calls, unpredictable sequencing) build the server unordered: `MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()`, so any incoming request matches any still-satisfiable expectation. `verify()` then confirms all counts were met. For different responses across retries you record multiple expectations (first fails, second succeeds) — with unordered/count semantics chosen to model the retry behavior.
code
java · 14 lines// Test a client that retries a GET up to 3 times on 5xx, then falls back.
MockRestServiceServer server = MockRestServiceServer
.bindTo(restTemplate)
.ignoreExpectOrder(true) // retries may not be strictly ordered
.build();
server.expect(ExpectedCount.times(3), requestTo("/quote"))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE));
String quote = quoteClient.getQuoteOrFallback();
assertThat(quote).isEqualTo("N/A"); // fallback after 3 failures
server.verify(); // confirms exactly 3 calls were madego deeper
Know expect() defaults to exactly one call and verify() checks it happened.
Use ExpectedCount variants and know ordering is strict by default.
Model retries/fallbacks/caching with counts + ordered/unordered managers correctly.
Standardize how the team asserts downstream call semantics (idempotency, retry ceilings, cache-hit no-call) and when MockRestServiceServer is insufficient vs contract/integration tests.
## ExpectedCount — how many times `MockRestServiceServer.expect(...)` has two overloads: - `expect(RequestMatcher)` — shorthand for **exactly once**. - `expect(ExpectedCount count, RequestMatcher)` — explicit count. `ExpectedCount` (in `org.springframework.test.web.client`) factory methods: - `once()` — exactly 1. - `manyTimes()` — 1 or more, unbounded. - `times(n)` — exactly n. - `min(n)` — at least n. - `max(n)` — at most n. - `between(min, max)` — inclusive range. - `never()` — the request must **not** occur. A single expectation with `times(3)` **absorbs three matching requests**. This is how you test a **retry policy**: if your client retries a GET up to 3 times on 5xx, record `expect(times(3), requestTo(url)).andRespond(withServerError())` and assert the client ultimately fails/falls back — or model the last attempt succeeding with separate expectations. ## Ordering — strict by default By default `MockRestServiceServer` matches requests **in the exact order the expectations were recorded** (ordered/`RequestExpectationManager` = `SimpleRequestExpectationManager` semantics). Request #1 from the client must satisfy expectation #1, etc. If the second real request doesn't match the second expectation, the test fails immediately. ### Unordered matching When request order is non-deterministic — retries with backoff, parallel calls, or you simply don't care — create an **unordered** server: ```java MockRestServiceServer server = MockRestServiceServer .bindTo(restTemplate) .ignoreExpectOrder(true) .build(); ``` `ignoreExpectOrder(true)` uses `UnorderedRequestExpectationManager`: each incoming request matches **any** recorded expectation that can still be satisfied (respecting its count). Note: under `@RestClientTest` the auto-configured server is ordered; when you need unordered you often bind manually (still against the auto-configured `RestTemplate`/`RestClient` request factory) or use `@RestClientTest` and re-bind. ## Modeling retries with different responses To test "fails once, then succeeds": ```java server.expect(once(), requestTo(url)).andRespond(withServerError()); server.expect(once(), requestTo(url)).andRespond(withSuccess(body, JSON)); ``` With ordered semantics the first call gets the 500, the retry gets the 200. Then `verify()` ensures both happened. ## Resetting `server.reset()` clears expectations and recorded requests (rarely needed since the slice resets per test). `verify()` asserts every expectation met its minimum count. ## Common gotchas - **`times(n)` under-called**: if the client calls fewer than n times, `verify()` fails — good for asserting exact retry counts. - **Ordered vs unordered mismatch**: retries with jitter can violate strict order; use `ignoreExpectOrder(true)`. - **`manyTimes()` never verifies an upper bound**: it accepts 1..∞, so it won't catch an over-eager client. Use `times`/`max` when you need a ceiling. - **`never()`** is powerful for asserting a code path did NOT call a downstream (e.g., cache hit). - **Response reuse**: each expectation's responder fires per matched request; with `times(3)` the same responder answers all three. ## When to use Reach for `ExpectedCount` + ordering control whenever behavior depends on *how many* and *in what order* your client calls a downstream: retry/backoff policies, idempotency, caching (assert `never()` on a hit), fan-out calls, and circuit-breaker fallbacks.
- How would you assert that a cache hit means NO downstream HTTP call is made?Record the expectation with ExpectedCount.never(): server.expect(never(), requestTo(url)). If the client calls out despite the cache, verify() (or the immediate match failure) fails the test.
- Your client retries with random jitter and the strict-order server fails intermittently. Fix?Build the server with .ignoreExpectOrder(true) so it uses the UnorderedRequestExpectationManager; any incoming request matches any still-satisfiable expectation, removing order sensitivity.
saying these in an interview costs you the question
- Thinking expect() can only match a single request
- Assuming request order never matters (it's strict by default)
- Using manyTimes() when you need to assert an exact/maximum count
- Believing you can't test retries with MockRestServiceServer