🔧 How to Mock node-fetch with Jest (2025 Guide)

If you’re writing unit tests in Node.js, chances are you’ve used node-fetch to make HTTP requests — and wondered how to mock it in your Jest tests.

As a senior developer, I’ve had to do this countless times in enterprise applications. Here’s the cleanest way to mock node-fetch using Jest, with reusable code snippets and explanations.


🎯 Why Mock node-fetch?

In unit tests, you don't want real HTTP calls. They’re slow, flaky, and introduce external dependencies. By mocking node-fetch, you can:

  • Control responses for different test scenarios
  • Avoid hitting real APIs
  • Ensure your business logic is correctly tested

📦 Prerequisites

First, make sure you have the right packages installed:

npm install --save node-fetch
npm install --save-dev jest

If you're using ESM, node-fetch v3+ is pure ESM, so ensure your Jest config supports ESM too.


✅ Mocking node-fetch with Jest (CommonJS Example)

Let’s walk through a simple example.

1. Create your module

// fetchUser.js
const fetch = require('node-fetch');

async function fetchUser(id) {
  const response = await fetch(`https://api.example.com/users/${id}`);
  if (!response.ok) throw new Error('Network response was not ok');
  return response.json();
}

module.exports = fetchUser;

2. Create your test with a mock

// fetchUser.test.js
const fetchUser = require('./fetchUser');
const fetch = require('node-fetch');

jest.mock('node-fetch');

describe('fetchUser', () => {
  it('returns user data on success', async () => {
    const mockResponse = {
      ok: true,
      json: jest.fn().mockResolvedValue({ id: 1, name: 'John Doe' }),
    };

    fetch.mockResolvedValue(mockResponse);

    const result = await fetchUser(1);

    expect(fetch).toHaveBeenCalledWith('https://api.example.com/users/1');
    expect(result).toEqual({ id: 1, name: 'John Doe' });
  });

  it('throws on network error', async () => {
    const mockResponse = { ok: false };
    fetch.mockResolvedValue(mockResponse);

    await expect(fetchUser(2)).rejects.toThrow('Network response was not ok');
  });
});

⚠️ Using ESM? Read This

If you're using node-fetch@3+ (ESM-only), use jest.mock with dynamic imports:

// fetchUser.mjs
import fetch from 'node-fetch';

export async function fetchUser(id) {
  const response = await fetch(`https://api.example.com/users/${id}`);
  if (!response.ok) throw new Error('Network error');
  return response.json();
}
// fetchUser.test.mjs
import { jest } from '@jest/globals';
import fetch from 'node-fetch';
import { fetchUser } from './fetchUser.mjs';

jest.unstable_mockModule('node-fetch', () => ({
  default: jest.fn(),
}));

describe('fetchUser', () => {
  it('works with ESM', async () => {
    const mockFetch = await import('node-fetch');
    mockFetch.default.mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ id: 1 }),
    });

    const result = await fetchUser(1);
    expect(result).toEqual({ id: 1 });
  });
});

🧠 Pro Tips

  • Use jest.clearAllMocks() if mocking multiple times in a single test file.
  • To mock different responses for different tests, define the mock inside each test block.
  • Consider wrapping fetch inside a utility function or service to simplify mocking.

📌 Conclusion

Mocking node-fetch with Jest is straightforward once you set it up right. Whether you're using CommonJS or ESM, Jest gives you full control over your HTTP behavior in tests.

This setup is clean, scalable, and avoids unnecessary dependencies. Your test suite (and your CI pipeline) will thank you.