If you’ve ever tried to run a unit test only to have it fail because a third-party API was down or your local database wasn’t configured, you’ve experienced the pain of tightly coupled code. This is exactly why I created this mocking dependencies in Jest tutorial. When we talk about unit testing, the goal is to test a specific piece of logic in isolation. If your test relies on a network call, it’s no longer a unit test—it’s an integration test.

In my experience building automation tools, mocking is the single most effective way to increase test speed and reliability. By replacing a real dependency with a ‘mock’ (a fake version that returns predictable data), you remove the unpredictability of the outside world. Before we dive into the mocks, if you are completely new to the framework, I highly recommend checking out my Jest unit testing tutorial for beginners to get your environment set up.

Prerequisites

To follow this tutorial, you’ll need the following installed on your machine:

Step 1: Understanding the Problem

Let’s look at a real-world scenario. Imagine we have a UserService that fetches a user from an external API using axios. We want to test the logic that formats the user’s name, but we don’t actually want to make a network request every time we run the test.

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

export const getUserName = async (id) => {
  const response = await axios.get(`/users/${id}`);
  return `User: ${response.data.name}`;
};

If we test this directly, our tests will be slow and brittle. This is where jest.mock() comes into play.

Step 2: Basic Mocking with jest.mock()

The most common way to handle dependencies is by mocking the entire module. By calling jest.mock('axios'), Jest automatically replaces all functions in that module with mock functions.

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

jest.mock('axios');

test('should return formatted user name', async () => {
  // Arrange: Tell the mock what to return
  axios.get.mockResolvedValue({ data: { name: 'Ajmani' } });

  // Act
  const result = await getUserName(1);

  // Assert
  expect(result).toBe('User: Ajmani');
  expect(axios.get).toHaveBeenCalledWith('/users/1');
});

As shown in the terminal output logic, we aren’t hitting a server; we’re hitting a JavaScript object that mimics the server. This makes the test execute in milliseconds.

Terminal screenshot showing successful Jest test output with mocked axios calls
Terminal screenshot showing successful Jest test output with mocked axios calls

Step 3: Spying on Methods with jest.spyOn()

Sometimes you don’t want to replace the entire module. You might want to keep the original implementation but track how many times a function was called. This is where jest.spyOn() is invaluable.

const calculator = {
  add: (a, b) => a + b,
};

test('should spy on the add method', () => {
  const spy = jest.spyOn(calculator, 'add');
  
  calculator.add(1, 2);
  
  expect(spy).toHaveBeenCalled();
  spy.mockRestore(); // Always restore the original function!
});

Step 4: Creating Manual Mocks (__mocks__)

For complex dependencies (like a database client or a cloud SDK), writing mockResolvedValue in every single test file becomes repetitive. I prefer using the __mocks__ folder pattern.

1. Create a folder named __mocks__ adjacent to the module you want to mock.
2. Create a file with the same name as the module (e.g., axios.js).
3. Export the mock implementation.

// __mocks__/axios.js
export default {
  get: jest.fn(() => Promise.resolve({ data: {} })),
  post: jest.fn(() => Promise.resolve({ data: {} })),
};

Now, whenever you call jest.mock('axios') in your tests, Jest will automatically use the implementation in your __mocks__ folder.

Pro Tips for Better Mocks

Troubleshooting Common Issues

Issue: jest.mock() isn’t working or the real module is still being called.
Solution: Remember that jest.mock() calls are hoisted to the top of the file. Ensure you aren’t trying to use the mock before it’s defined. Also, double-check your import paths.

Issue: Mocking ES6 modules in a Node environment.
Solution: Depending on your Babel or TS configuration, you might need to use jest.doMock() if you need to mock modules dynamically within a test block.

What’s Next?

Now that you’ve mastered mocking, you might be wondering about the performance of your test suite as it grows. I’ve done a detailed comparison on is Vitest faster than Jest, which is worth reading if you’re working in a Vite-based project. Additionally, consider exploring Integration Testing to ensure that your mocks actually align with the real-world behavior of your dependencies.

Ready to level up your automation? Subscribe to my newsletter for weekly tips on development and productivity tools!