Test retry and back-off logic

A mock that fails first, then succeeds — so you can prove your retry code works.

Retry code is hard to test because real APIs rarely fail when you want them to. A mock with a response sequence fixes that: the first call returns 500, the next returns 200, and the cycle repeats. That gives you a deterministic failure your test suite can rely on, so you can prove the retry actually happened and that the second attempt succeeded — rather than assuming it would.

How do you make a call fail once and then succeed?

Define the default response as the failure and add a response sequence for what comes after it. The mock walks the sequence per call and then starts over, so a single endpoint produces the fail-then-succeed pattern every retry test needs, in a fixed order, with no randomness to make the test flaky.

Determinism is the whole point. Testing retries against a genuinely flaky endpoint gives you a suite that passes or fails for reasons unrelated to your code. If you do want randomness — for a soak test rather than a unit test — an error rate is the right tool instead of a sequence.

How do you verify back-off and not just retry count?

Counting attempts proves you retried; it does not prove you waited. Record the timestamp of each delivery in the inspector and check the gaps grow the way your policy says they should. Fixed-interval retries from every client at once are what turn a brief outage into a sustained one, which is why exponential back-off with jitter is the standard advice.

Add a response delay to the mock to test the other half of the problem: a request that hangs rather than fails. A client with retries but no timeout will wait indefinitely on the first attempt and never reach the retry at all.

Which failures should actually be retried?

Retrying everything is as wrong as retrying nothing. A 500 or 503 is worth another attempt; a 400 or 422 will fail identically forever and a retry only multiplies the error. 429 is its own case — honour the Retry-After header rather than applying your own schedule.

Mock each of these as a separate endpoint and assert on the behaviour you want: retried, not retried, or deferred until the server says so. Three small mocks document your retry policy more precisely than any comment.

Past a point, stop retrying and fail loudly. A circuit breaker that opens after repeated failures protects both you and the service you are calling: retrying forever converts a dependency's outage into your own, and hides it from your alerting while it does so.

What usually goes wrong?

  • Retrying non-idempotent requests. A retried POST can charge a card twice. Retry safely only where a repeat is harmless, or use an idempotency key.
  • No cap. Retries without a ceiling turn a short outage into an outage of your own making.
  • Retry without timeout. If the first attempt never returns, the retry never runs — test the hang, not only the failure.
  • Synchronised retries. Without jitter every client retries on the same beat and the recovering service is hit by the whole fleet at once.

When to use this

Retry code is notoriously untested because real APIs rarely fail on demand. This mock returns 500 on the first call and 200 on the second, then cycles, so an automated test can assert your client retries and recovers. The X-Mockapi-Variant header shows which step served each hit.

Create the mock

curl -X POST https://quickmock.dev/api/mocks \
  -H 'Content-Type: application/json' \
  -d '{
  "method": "GET",
  "response_status": 500,
  "content_type": "application/json",
  "response_body": "{\"error\":\"upstream\"}",
  "response_sequence": [
    { "status": 200, "body": "{\"ok\":true}" }
  ]
}'

Call it

curl https://quickmock.dev/m/<slug>

What you get

1st call  -> 500  (X-Mockapi-Variant: seq-1/2)
2nd call  -> 200  (seq-2/2)
...then it cycles

FAQ

Is the failure order deterministic?
Yes. A response sequence is walked in order, one step per call, then repeats — so the same test produces the same sequence of statuses every run. Use an error rate instead only when you deliberately want randomness.
How do I check how many times my client retried?
The inspector lists every delivery with its timestamp, so the count is the number of requests and the back-off is the gaps between them. That is also how you catch a client retrying far more often than intended.
Can I simulate a hanging request rather than an error?
Yes — set a response delay. It is the case most retry tests miss: a client with no timeout blocks on the first attempt forever and its retry logic never executes.

Build your own

Create a mock

Related guides

← All guides

Last updated: 2026-09-08