Testing React applications effectively requires the ability to isolate components and their dependencies. Often, components interact with external modules, APIs, or complex logic that you don’t want to execute during testing. Jest, one of the most popular testing frameworks for JavaScript and React, provides mock functions and module mocking capabilities to handle these cases efficiently.
In this detailed guide, we’ll explore how Jest mock functions work, how to mock modules, and how to apply them in real-world testing scenarios. By the end of this post, you’ll have a strong understanding of how to use mocks to create cleaner, more reliable, and more maintainable unit tests.
1. Introduction to Jest Mocks
When writing unit tests, the goal is to test a specific piece of functionality in isolation. However, React components or JavaScript modules often depend on external data, APIs, or helper functions. Calling these dependencies directly can cause tests to become slow, unreliable, or dependent on factors outside of the test environment.
Jest’s mocking capabilities solve this by allowing you to replace real implementations with “fake” ones that you can control, track, and inspect.
Mock functions make it possible to:
- Test how a function was called.
- Simulate return values or errors.
- Verify component interactions without relying on real modules or APIs.
Jest mocks are powerful tools for ensuring that your tests are fast, focused, and predictable.
2. Creating Mock Functions with jest.fn()
The simplest way to create a mock function in Jest is by using jest.fn(). A mock function records how it’s called, how many times it’s called, and what arguments are passed to it.
Example: Basic Mock Function
const mockCallback = jest.fn();
mockCallback('hello');
expect(mockCallback).toHaveBeenCalledWith('hello');
Explanation:
jest.fn()creates a new mock function.mockCallback('hello')calls the function with'hello'as an argument.expect(mockCallback).toHaveBeenCalledWith('hello')checks whether it was called with that specific value.
This example demonstrates how Jest allows you to track function calls easily. You can use additional matchers like:
.toHaveBeenCalledTimes(number)– to check how many times a mock was called..toHaveBeenCalledWith(arg1, arg2, ...)– to verify arguments passed..toHaveReturnedWith(value)– to confirm a specific return value.
3. Adding Custom Implementations
You can also define what a mock function should return or how it should behave using mockImplementation().
Example: Custom Implementation
const mockAdd = jest.fn().mockImplementation((a, b) => a + b);
expect(mockAdd(2, 3)).toBe(5);
expect(mockAdd).toHaveBeenCalledTimes(1);
In this case:
- The mock behaves like a real function that adds two numbers.
- You can still inspect its call count, arguments, and results.
This is especially useful when you need to simulate specific logic without executing real code.
4. Mocking Return Values
If you want your mock function to return specific values, you can use:
mockReturnValue(value)mockReturnValueOnce(value)
Example: Controlling Return Values
const mockFunction = jest.fn();
mockFunction.mockReturnValueOnce('First').mockReturnValueOnce('Second');
expect(mockFunction()).toBe('First');
expect(mockFunction()).toBe('Second');
This is particularly helpful when testing asynchronous operations or multiple calls where each should yield a different response.
5. Mocking Async Functions
Asynchronous operations like API calls can also be mocked easily. This allows you to simulate successful responses or errors.
Example: Mocking Promises
const mockApiCall = jest.fn().mockResolvedValue({ data: 'Success' });
test('mocks async call', async () => {
const response = await mockApiCall();
expect(response.data).toBe('Success');
expect(mockApiCall).toHaveBeenCalled();
});
You can also use mockRejectedValue() to simulate errors.
const mockApiCall = jest.fn().mockRejectedValue(new Error('Network Error'));
test('handles API error', async () => {
try {
await mockApiCall();
} catch (error) {
expect(error.message).toBe('Network Error');
}
});
This approach ensures your component’s error-handling logic works correctly without hitting real endpoints.
6. Mocking Modules with jest.mock()
Sometimes, a function is imported from another module, and you want to replace it entirely with a mock. Jest allows you to mock entire modules using jest.mock().
Example: Mocking a Module
jest.mock('./myModule');
const myModule = require('./myModule');
myModule.fetchData.mockResolvedValue('Mocked Data');
test('mocks external module', async () => {
const data = await myModule.fetchData();
expect(data).toBe('Mocked Data');
});
Here’s what happens:
- The real
myModulefile is replaced with a mock. - All functions exported from it become mock functions.
- You can define how they behave in your test.
This helps isolate your component or function from external dependencies, such as network APIs or utility modules.
7. Mocking Named Exports
If the module exports multiple functions, you can mock each one individually.
jest.mock('./api', () => ({
fetchUser: jest.fn().mockResolvedValue({ name: 'Alice' }),
updateUser: jest.fn().mockResolvedValue({ success: true }),
}));
Now both fetchUser and updateUser are mocked and return predefined values.
8. Example: Mocking an API Module in a React Component Test
Let’s see how to mock a module inside a React component test.
api.js
export const fetchUser = async () => {
const response = await fetch('/api/user');
return response.json();
};
UserProfile.js
import React, { useEffect, useState } from 'react';
import { fetchUser } from './api';
export default function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then((data) => setUser(data));
}, []);
if (!user) return <p>Loading...</p>;
return <h1>{user.name}</h1>;
}
UserProfile.test.js
import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';
import { fetchUser } from './api';
jest.mock('./api');
test('renders user profile after fetching data', async () => {
fetchUser.mockResolvedValue({ name: 'John Doe' });
render(<UserProfile />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
const userName = await waitFor(() => screen.getByText(/john doe/i));
expect(userName).toBeInTheDocument();
});
What’s Happening Here:
- The
fetchUserfunction is mocked to avoid making a real network request. - The mock returns
{ name: 'John Doe' }. - The test ensures that “Loading” appears initially, and then “John Doe” is rendered after the mock resolves.
This pattern is common when testing components that fetch or process data.
9. Mocking Axios or Fetch Calls
If your application uses Axios or fetch, you can also mock them using Jest.
Example: Mocking Axios
import axios from 'axios';
import { fetchData } from './dataService';
jest.mock('axios');
test('mocks axios get call', async () => {
axios.get.mockResolvedValue({ data: { message: 'Hello Jest' } });
const response = await fetchData();
expect(response.message).toBe('Hello Jest');
});
This ensures that no actual HTTP request is made during testing.
10. Spying on Functions
Sometimes, instead of replacing a function, you want to observe its calls while keeping its original implementation. You can use jest.spyOn() for this.
Example: Using jest.spyOn
const mathUtils = {
add: (a, b) => a + b,
};
test('spies on mathUtils.add', () => {
const spy = jest.spyOn(mathUtils, 'add');
mathUtils.add(2, 3);
expect(spy).toHaveBeenCalledWith(2, 3);
});
Spies allow you to track function calls and arguments without fully mocking them, which is useful for verifying side effects.
11. Clearing and Resetting Mocks
When running multiple tests, mock functions can retain information between tests. Jest provides methods to clear or reset mocks to maintain test isolation.
Common Methods
mockFn.mockClear()– Clears call history but keeps implementation.mockFn.mockReset()– Clears call history and implementation.mockFn.mockRestore()– Restores the original (non-mocked) implementation.
Example:
afterEach(() => {
jest.clearAllMocks();
});
This ensures that each test runs in a clean environment.
12. Combining Mocks with React Testing Library
You can combine Jest mocks with React Testing Library to test component interactions with external dependencies.
For example:
- Mock an API call and verify the component renders data correctly.
- Mock a function passed as a prop and check if it’s called after a button click.
Example:
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
function Button({ onClick }) {
return <button onClick={onClick}>Click</button>;
}
test('calls mock function when button clicked', async () => {
const mockClick = jest.fn();
render(<Button onClick={mockClick} />);
await userEvent.click(screen.getByText(/click/i));
expect(mockClick).toHaveBeenCalledTimes(1);
});
This pattern is essential for testing callback props in React.
13. Common Mistakes When Using Jest Mocks
- Forgetting to reset mocks between tests.
- Over-mocking modules, making tests unrealistic.
- Mocking implementation details that should be tested.
- Not asserting how mocks were used (e.g., missing
.toHaveBeenCalled()checks). - Using mocks for integration tests where real interactions are needed.
Keep mocks focused and meaningful — use them only where necessary.
14. Best Practices for Using Jest Mocks
- Mock only what you control.
Avoid mocking libraries or built-in browser APIs unless absolutely necessary. - Keep tests deterministic.
Return predictable values from mocks to avoid flaky tests. - Use descriptive mock names.
It helps maintain readability in large test suites. - Combine mocks and spies appropriately.
Use spies when you need to observe real behavior, mocks when you need full control. - Avoid deep mocking.
If you find yourself mocking multiple layers of a module, consider refactoring your code for better testability.
Leave a Reply