Mocking Axios in Jest tests is a surprisingly straightforward process once you grasp the core mechanism: Jest’s module mocking. The trick isn’t in Axios itself, but in how you tell Jest to substitute the real Axios with your controlled test version.

Let’s see it in action. Imagine you have a service that fetches user data:

// userService.js
import axios from 'axios';

export async function getUser(userId) {
  try {
    const response = await axios.get(`/api/users/${userId}`);
    return response.data;
  } catch (error) {
    console.error("Error fetching user:", error);
    throw error;
  }
}

Now, for testing getUser without hitting a real server, we’ll mock axios.

First, we need to set up the mock in our test file. Jest’s jest.mock() function is the key. It intercepts require or import calls for the specified module.

// userService.test.js
import { getUser } from './userService';
import axios from 'axios';

// Tell Jest to mock the 'axios' module
jest.mock('axios');

// Now, 'axios' inside this file will be our mock
const mockedAxios = axios;

With axios mocked, Jest replaces the actual axios module with an empty object by default. To make our mock do something useful, we need to define its behavior. We can do this by accessing the mocked module’s methods.

// userService.test.js (continued)

describe('getUser', () => {
  it('should fetch user data successfully', async () => {
    const mockUserData = { id: 1, name: 'Alice' };
    const userId = 123;

    // Configure the mock's 'get' method
    // We're telling it that when .get is called, it should return a Promise
    // that resolves with an object containing our mock data.
    mockedAxios.get.mockResolvedValue({ data: mockUserData });

    const user = await getUser(userId);

    // Assert that axios.get was called with the correct URL
    expect(mockedAxios.get).toHaveBeenCalledTimes(1);
    expect(mockedAxios.get).toHaveBeenCalledWith(`/api/users/${userId}`);

    // Assert that our function returned the correct data
    expect(user).toEqual(mockUserData);
  });

  it('should handle API errors', async () => {
    const userId = 456;
    const errorMessage = 'Network Error';

    // Configure the mock to reject with an error
    mockedAxios.get.mockRejectedValue(new Error(errorMessage));

    // Expect the function to throw an error
    await expect(getUser(userId)).rejects.toThrow(errorMessage);

    expect(mockedAxios.get).toHaveBeenCalledTimes(1);
    expect(mockedAxios.get).toHaveBeenCalledWith(`/api/users/${userId}`);
  });
});

The core mental model is this: jest.mock('axios') tells Jest "anywhere axios is imported in this test file (or files imported by it, if you use jest.unmock carefully), use this fake version instead." Then, mockedAxios.get.mockResolvedValue(...) and mockedAxios.get.mockRejectedValue(...) are how you tell that fake version what to do when its methods are called. You’re essentially controlling the return value of axios.get (or post, put, etc.) for the duration of your test.

This allows you to test:

  • Successful API calls: What happens when the API returns data?
  • API errors: What happens when the API returns an error status or network issues occur?
  • Specific response structures: Does your code correctly parse different response.data shapes?
  • Request parameters: Are you sending the correct data, headers, or URLs in your requests?

You can mock specific methods or the entire module. For complex scenarios where axios might be used in multiple places with different configurations, you can even provide a factory function to jest.mock:

// In your test file:
jest.mock('axios', () => ({
  get: jest.fn(() => Promise.resolve({ data: { id: 99, name: 'Mocked User' } })),
  post: jest.fn(() => Promise.resolve({ status: 201 })),
  // ... other methods you need to mock
}));

// Then in your test, you can access the specific mocks
const mockedAxios = require('axios'); // or import axios from 'axios';
mockedAxios.get.mockResolvedValue(...) // This works too

The trick that trips people up is understanding that jest.mock is hoisted to the top of the scope by Jest. This means you can call jest.mock('axios') after you import axios from 'axios', but the mock will still be applied correctly. It’s a bit of syntactic sugar that can feel like magic.

When you need to mock different behaviors for different tests within the same file, use jest.resetModules() before require or importing the module you want to test, and then jest.mock() again. Or, more commonly, use beforeEach to reset mocks:

// userService.test.js (continued)
describe('getUser', () => {
  beforeEach(() => {
    // Reset mock implementations before each test
    mockedAxios.get.mockClear();
  });

  it('should fetch user data successfully', async () => {
    // ... test logic ...
  });

  it('should handle API errors', async () => {
    // ... test logic ...
  });
});

This ensures that the mock’s call history and resolved/rejected values are clean for each individual test, preventing side effects between tests.

Once you’ve mastered mocking axios with Jest, you’ll find it straightforward to mock any other Node.js module or third-party library that uses require or import.

Want structured learning?

Take the full Jest course →