Honest split first: axios-mock-adapter earned its ~2.8M weekly downloads. mock.onGet("/users").reply(200, users) is about as pleasant as stubbing gets, and for fast offline unit tests of axios-based code it's still a fine choice. This page is not going to pretend otherwise.
We verified all of these the week of writing (Aug 2026, axios-mock-adapter v2.1.0, axios v1, Node 22) โ every snippet on this page was actually run:
axios.create() instance and stubbed /products: the wrapped instance got the stub, a second axios instance went to the real network, and native fetch went to the real network. If your app mixes fetch and axios, or an SDK creates its own internal instance, those requests sail straight past your mock.new AxiosMockAdapter(axios) wraps the default instance โ every module in the process doing require("axios").get(โฆ) is now mocked until you mock.restore().onGet("/products", { params: { page: 1 } }) and requested the same URL with one extra param: Request failed with status code 404. Params must match exactly, and the default onNoMatch turns every miss into a mystery 404 that looks like a backend bug. (Set onNoMatch: "throwException" to at least get a useful error.)reply() answers forever, replyOnce() is consumed after one match; networkError() rejects with a bare "Network Error" (no code); timeout() rejects with ECONNABORTED instantly โ no time actually passes, so timeout durations are never really exercised.If the first two bullets are your actual problem, no adapter option fixes them โ the mock needs to be somewhere every client can reach:
Mockbird is the other half: a hosted mock REST API on a real URL. Define resources (or pick a preset, or import an OpenAPI spec / db.json / CSV / Postman collection), get realistic seeded data and full stateful CRUD โ reachable from axios, fetch, a browser, curl, a phone, another service, CI, or a teammate. No adapter code, no signup.
# one curl, no signup โ a whole seeded e-commerce backend
curl -X POST https://mockbird.mockbird.workers.dev/api/projects \
-H 'content-type: application/json' -d '{"name":"shop","preset":"ecommerce"}'
# โ {"id":"abc123","adminKey":"KEY", ...} โ save both
curl https://mockbird.mockbird.workers.dev/m/abc123/products?limit=3
# โ 3 seeded products, CORS on, writes persist
Then in your app, nothing changes but the base URL:
const api = axios.create({ baseURL: process.env.API_URL });
// dev/test: API_URL=https://mockbird.mockbird.workers.dev/m/abc123
// prod: API_URL=https://api.yourapp.com
Prefer clicking? This link creates the same project in your browser โ no account. Have an OpenAPI spec? Paste it at /app#import and get a live seeded mock of your actual API shape.
| axios-mock-adapter | Mockbird | Notes |
|---|---|---|
new AxiosMockAdapter(client) | no code โ point baseURL at the mock | no wrapped-instance boundary: fetch, SDKs, curl, teammates all see the same mock |
onGet("/products").reply(200, data) | a products resource | list/get/create/update/delete generated, plus filtering, sorting, pagination, relations |
| Hand-rolled reply data | seeded realistic records | faker-style names/emails/prices/dates; or import your exact records from db.json/CSV |
reply(500) | ?mock_status=500 on any URL | no adapter edit โ error/loading states guide |
networkError() / flaky-API simulation | ?mock_chaos=0.3 | fails a random fraction with real 5xx/429 responses โ point your retry logic at it (honest note: that's HTTP-level failure, not a socket-level network error) |
timeout() / delayResponse | ?mock_delay=3000 | real time passes, so a 1s axios timeout genuinely fires โ plus ?mock_jitter=100-1500 random latency |
| Exact-param matching misses โ 404 | not needed | any query composes: filters, ?select=, sort, pagination โ no "why is this 404ing" |
mock.history.get | request inspector | last 50 requests: method, path, query, headers, body โ assert what was actually sent |
reset() / resetHistory() between tests | snapshot pinning | save named data states; each parallel test pins one via X-Mockbird-Snapshot โ no reset races |
restore() | flip the env var back | your app code never knew the difference |
# seeded data with field projection
curl 'https://mockbird.mockbird.workers.dev/m/demo/products?limit=2&select=name,price'
# the states you'd write adapter handlers for โ as query params
curl -i 'https://mockbird.mockbird.workers.dev/m/demo/products?mock_status=503'
curl 'https://mockbird.mockbird.workers.dev/m/demo/products/1?mock_delay=2000'
# stateful: the write persists (the thing the adapter can't do)
curl -X POST https://mockbird.mockbird.workers.dev/m/demo/products \
-H 'content-type: application/json' -d '{"name":"proof","price":9.99}'
# โ returns {"id":31,โฆ} โ and GET /m/demo/products/31 now works
All against the shared demo project (resets daily).
timeout() rejects instantly โ your abort/timeout duration logic never runs. Against a hosted URL, ?mock_delay=3000 makes a 1s axios timeout actually fire, and ?mock_chaos=0.5 gives your axios-retry backoff real 5xx storms to absorb โ recipes in the Node.js guide.GET /m/<project>/msw.js returns an MSW v2 handlers module with your project's current data baked in (MSW intercepts axios too) โ shape data in the dashboard, then run offline unit tests from generated code. No lock-in in either direction.| axios-mock-adapter | Mockbird | |
|---|---|---|
| What it is | free OSS library (axios only) | free hosted service |
| Reachable from | the axios instance you wrap | anything with HTTP: fetch, browser, curl, Postman, mobile, backend, CI, agents |
| fetch / other HTTP clients | not intercepted (verified) | it's a real URL โ every client works by definition |
| Setup | write + maintain handlers | one curl or one click; no code |
| Mock data | you write it | seeded realistic data, or import your own |
| Stateful CRUD | DIY | default โ writes persist |
| Error/latency simulation | per-handler code; timeout() is instant | ?mock_status / ?mock_delay / ?mock_chaos / ?mock_jitter on any URL, real time passes |
| Unmatched request | synthesized 404 (default) โ looks like a backend bug | n/a โ real endpoints answer real queries |
| Works offline | yes | no โ it's a real network call |
| Latency | zero (in-process) | real network latency |
| Request assertions | mock.history, precise | request inspector (last 50, with headers/body) |
| Last release | v2.1.0, Oct 2024 (npm registry, as of Aug 2026) | actively developed |
| Request cap | none | 10,000/project/day |
Written by the Mockbird maker โ bias disclosed. Where axios-mock-adapter genuinely wins: in-process interception means zero latency, zero network flakiness, tests that run on a plane; networkError() is a true request-level failure with no HTTP response, which we can't produce; mock.history assertions are more precise than any log-based check; and a reply function is arbitrary JavaScript. For offline unit tests of axios-only code, keep it. When the thing you need is a URL โ for fetch, a browser, a prototype, a teammate, a mobile client, or CI hitting a deployed build โ that's us.
Full API reference in the docs. More guides: mock API for Node.js ยท nock alternative ยท MSW alternative ยท mock APIs in Jest ยท mock APIs in Vitest ยท free mock API tools compared ยท deterministic test data ยท testing loading & error states. Create your API โ