fetch-mock vs axios-mock-adapter
Mocking HTTP Requests in Frontend Testing
fetch-mockaxios-mock-adapterSimilar Packages:

Mocking HTTP Requests in Frontend Testing

axios-mock-adapter and fetch-mock are both widely used libraries for intercepting and mocking HTTP requests during frontend testing, but they target different underlying HTTP clients. axios-mock-adapter is specifically designed to mock requests made with the axios HTTP client by hooking into its request interceptor system. In contrast, fetch-mock mocks the native window.fetch API (or Node.js equivalents), making it suitable for applications that rely directly on fetch without an abstraction layer. Both enable deterministic test environments by simulating network responses without hitting real endpoints.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
fetch-mock1,256,8461,309157 kB49 months agoMIT
axios-mock-adapter03,54467.9 kB962 years agoMIT

Mocking HTTP Requests: axios-mock-adapter vs fetch-mock

When writing frontend tests, you often need to simulate network responses without making real HTTP calls. Both axios-mock-adapter and fetch-mock solve this problem — but they operate at different layers of the networking stack. Choosing the right tool depends entirely on which HTTP client your app actually uses.

🧪 Core Target: What Each Library Intercepts

axios-mock-adapter only mocks requests made through axios. It works by attaching to axios’s internal interceptor pipeline, so it never touches the actual network layer.

// axios-mock-adapter only affects axios calls
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

const mock = new MockAdapter(axios);
mock.onGet('/users').reply(200, [{ id: 1, name: 'Alice' }]);

// This is mocked
const res = await axios.get('/users');

// But this bypasses the mock entirely
const realRes = await fetch('/users'); // hits the real network!

fetch-mock mocks the global fetch function itself. It replaces window.fetch (or global.fetch in Node) so any code using fetch — including third-party libraries — will be intercepted.

// fetch-mock affects all fetch calls
import fetchMock from 'fetch-mock';

fetchMock.get('/users', [{ id: 1, name: 'Alice' }]);

// This is mocked
const res = await fetch('/users');

// So is this (if some-lib uses fetch internally)
const libRes = await someLibraryThatUsesFetch();

// But axios calls are unaffected
const axiosRes = await axios.get('/users'); // real network call

💡 Key Insight: These tools are not interchangeable. Use axios-mock-adapter only with axios. Use fetch-mock only with fetch. Mixing them leads to unmocked requests and flaky tests.

🔍 Route Matching: How Patterns Are Defined

Both libraries support flexible route matching, but their syntax differs based on their target APIs.

axios-mock-adapter matches based on method + URL, and supports regex, wildcards, and custom matchers:

const mock = new MockAdapter(axios);

// Exact match
mock.onGet('/users').reply(200, users);

// Regex match
mock.onGet(/\/users\/\d+/).reply(200, user);

// With query params
mock.onGet('/search', { q: 'react' }).reply(200, results);

// Custom matcher function
mock.onGet((config) => config.url.includes('admin')).reply(403);

fetch-mock uses a matcher-first approach where the first argument defines what to intercept:

// String path
fetchMock.get('/users', users);

// Regex
fetchMock.get(/\/users\/\d+/, user);

// Path with query
fetchMock.get('/search?q=react', results);

// Custom matcher function
fetchMock.get((url, opts) => url.includes('admin'), 403);

// Also supports POST body matching
fetchMock.post('/login', (url, opts) => {
  return opts.body.includes('valid-token');
}, { status: 200 });

Both handle dynamic segments well, but fetch-mock’s matcher functions receive more context (like request options), which can be useful for complex assertions.

🧪 Test Isolation and Cleanup

Clean test isolation prevents state leakage between test cases — a common source of flakiness.

axios-mock-adapter requires manual cleanup:

let mock;

beforeEach(() => {
  mock = new MockAdapter(axios);
});

afterEach(() => {
  mock.restore(); // removes interceptors
});

// Or reset history
mock.reset(); // clears recorded calls but keeps routes

fetch-mock provides built-in sandboxing and automatic cleanup:

// Option 1: Global instance (requires manual reset)
afterEach(() => fetchMock.reset());

// Option 2: Create isolated sandboxes per test
const fetchMockSandbox = fetchMock.sandbox();
fetchMockSandbox.get('/users', users);

// This sandbox doesn’t affect other tests
await fetchMockSandbox('/users');

The sandbox feature in fetch-mock is especially valuable in large test suites where concurrent or overlapping mocks could interfere.

📦 Handling Request Bodies and Headers

Both libraries let you inspect and assert on request details.

axios-mock-adapter exposes request data via the config object in custom handlers:

mock.onPost('/login').reply((config) => {
  const { username, password } = JSON.parse(config.data);
  if (username === 'admin') {
    return [200, { token: 'fake-jwt' }];
  }
  return [401];
});

// Later, assert what was sent
expect(mock.history.post[0].data).toBe(JSON.stringify({ username: 'admin' }));

fetch-mock gives access to the full Request object in matchers and allows inspection via .calls():

fetchMock.post('/login', (url, opts) => {
  const body = JSON.parse(opts.body);
  return body.username === 'admin' ? { token: 'fake-jwt' } : 401;
});

// Assert on actual calls
const calls = fetchMock.calls('/login');
expect(JSON.parse(calls[0][1].body)).toEqual({ username: 'admin' });

Both approaches work well, but fetch-mock’s use of standard fetch options aligns more closely with browser APIs.

🌐 Environment Support

axios-mock-adapter works wherever axios runs — browser or Node.js — because it operates at the axios layer, not the network layer.

fetch-mock requires a fetch implementation. In Node.js, you must provide one (e.g., node-fetch, undici, or cross-fetch). The library itself doesn’t include a polyfill.

// In Node.js test setup
global.fetch = require('node-fetch');
// Now fetch-mock works

This isn’t a limitation per se, but it’s a setup step you must handle explicitly when testing non-browser environments.

🔄 Real-World Usage Scenarios

Scenario 1: React App Using Axios

You’re building a dashboard with axios for all API calls.

  • Use axios-mock-adapter
  • Why? Direct integration, no extra polyfills, and clean alignment with your HTTP layer.
// test/utils/mockApi.js
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

export const mockApi = () => {
  const mock = new MockAdapter(axios);
  mock.onGet('/profile').reply(200, { name: 'Dev' });
  return mock;
};

Scenario 2: Remix or Next.js App Using Native Fetch

Your framework encourages direct fetch usage (e.g., Remix loaders).

  • Use fetch-mock
  • Why? It mocks the actual primitive your app relies on, and works seamlessly with framework conventions.
// app/routes/dashboard.test.jsx
import fetchMock from 'fetch-mock';

beforeEach(() => {
  fetchMock.get('/api/user', { name: 'Tester' });
});

afterEach(() => fetchMock.reset());

Scenario 3: Mixed HTTP Clients (Not Recommended)

Your codebase uses both axios and fetch.

  • ⚠️ You’ll need both libraries — but this is a code smell.
  • Better fix: Standardize on one HTTP client to simplify testing and maintenance.

📊 Summary Table

Featureaxios-mock-adapterfetch-mock
Mocksaxios requests onlynative fetch calls
Setup ComplexityLow (just pass axios instance)Medium (may need fetch polyfill in Node)
Route MatchingMethod + URL + custom functionsFlexible matchers (string, regex, fn)
Test IsolationManual (restore(), reset())Built-in sandboxes + reset()
Request InspectionVia mock.historyVia fetchMock.calls()
Works in Node.jsYes (via axios)Yes (with fetch polyfill)
Third-Party Lib SupportOnly if they use your axios instanceYes (if they use fetch)

💡 Final Recommendation

Don’t choose based on popularity — choose based on your actual HTTP client:

  • If you use axios, reach for axios-mock-adapter. It’s purpose-built, lightweight, and integrates cleanly.
  • If you use fetch, use fetch-mock. It’s the standard way to mock the native API reliably.

Trying to force one to mock the other’s domain leads to gaps in coverage and confusing test failures. Keep your mocking layer aligned with your networking layer — your future self (and teammates) will thank you.

How to Choose: fetch-mock vs axios-mock-adapter

  • fetch-mock:

    Choose fetch-mock if your project uses the native fetch API (with or without lightweight wrappers) or runs in environments where fetch is the standard networking primitive (e.g., modern browsers, service workers, or frameworks like Remix). It offers fine-grained control over route matching, supports sandboxed mocking for isolated tests, and works consistently across browser and Node.js environments via compatible fetch implementations. Don’t use it to mock axios requests unless you’ve polyfilled or replaced axios with fetch under the hood.

  • axios-mock-adapter:

    Choose axios-mock-adapter if your application uses axios as its primary HTTP client and you want a tightly integrated mocking solution that leverages axios’s interceptor architecture. It provides a clean, chainable API that mirrors axios’s own patterns and handles edge cases like request cancellation and custom instance configuration naturally. Avoid it if your codebase doesn’t use axios — it won’t intercept fetch calls.

README for fetch-mock

fetch-mock

Features include:

  • mocks most of the fetch API spec, even advanced behaviours such as streaming and aborting
  • declarative matching for most aspects of a http request, including url, headers, body and query parameters
  • shorthands for the most commonly used features, such as matching a http method or matching one fetch only
  • support for delaying responses, or using your own async functions to define custom race conditions
  • can be used as a spy to observe real network requests
  • can be extended with your own reusable custom matchers that can be used both for matching fetch-calls and inspecting the results
  • isomorphic, and supports either a global fetch instance or a locally required instance

Requirements

@fetch-mock requires either of the following to run:

  • Node.js 18+ for full feature operation
  • Any modern browser that supports the fetch API
  • node-fetch when testing in earlier versions of Node.js (this is untested, but should mostly work)

Documentation and Usage

See the project website

License

fetch-mock is licensed under the MIT license. Copyright © 2024, Rhys Evans