Mocking Basics
Mocking is a powerful technique that lets you replace real implementations with controlled test doubles. This is essential for testing code that has dependencies like APIs, databases, or other modules.
Why Mock?
Consider this function:
async function getUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
return {
...user,
fullName: `${user.firstName} ${user.lastName}`
};
}
Without mocking, testing this would require:
- A running server
- Network connectivity
- Consistent test data
With mocking, you can:
- Control exactly what data is returned
- Test error scenarios
- Run tests instantly without network
Jest Mock Functions
Create mock functions with jest.fn():
// Create a mock function
const mockCallback = jest.fn();
// Call it
mockCallback('hello');
mockCallback('world');
// Check how it was called
expect(mockCallback).toHaveBeenCalled();
expect(mockCallback).toHaveBeenCalledTimes(2);
expect(mockCallback).toHaveBeenCalledWith('hello');
expect(mockCallback).toHaveBeenLastCalledWith('world');
Mock Return Values
const mockFn = jest.fn();
// Return a specific value
mockFn.mockReturnValue(42);
expect(mockFn()).toBe(42);
// Return different values on successive calls
mockFn.mockReturnValueOnce(1)
.mockReturnValueOnce(2)
.mockReturnValue(0);
expect(mockFn()).toBe(1);
expect(mockFn()).toBe(2);
expect(mockFn()).toBe(0);
expect(mockFn()).toBe(0); // Default after once values
Mock Implementations
const mockFn = jest.fn((x) => x * 2);
expect(mockFn(5)).toBe(10);
// Change implementation
mockFn.mockImplementation((x) => x + 1);
expect(mockFn(5)).toBe(6);
// One-time implementation
mockFn.mockImplementationOnce((x) => x * 10);
expect(mockFn(5)).toBe(50);
expect(mockFn(5)).toBe(6); // Back to previous implementation
Mocking Modules
Auto-mocking a Module
// Mock the entire axios module
jest.mock('axios');
const axios = require('axios');
describe('API calls', () => {
it('fetches users', async () => {
// Set up the mock response
axios.get.mockResolvedValue({
data: [{ id: 1, name: 'Alice' }]
});
const users = await fetchUsers();
expect(axios.get).toHaveBeenCalledWith('/api/users');
expect(users).toHaveLength(1);
});
});
Mocking with Factory Function
jest.mock('./database', () => ({
query: jest.fn(),
connect: jest.fn(),
disconnect: jest.fn()
}));
const db = require('./database');
describe('Database operations', () => {
it('queries the database', async () => {
db.query.mockResolvedValue([{ id: 1 }]);
const result = await getUsers();
expect(db.query).toHaveBeenCalledWith('SELECT * FROM users');
expect(result).toHaveLength(1);
});
});
Spying on Methods
jest.spyOn lets you track calls to existing methods:
const calculator = {
add: (a, b) => a + b,
multiply: (a, b) => a * b
};
describe('calculator', () => {
it('tracks method calls', () => {
const addSpy = jest.spyOn(calculator, 'add');
calculator.add(2, 3);
expect(addSpy).toHaveBeenCalledWith(2, 3);
expect(addSpy).toHaveReturnedWith(5);
addSpy.mockRestore(); // Clean up
});
it('can override implementation', () => {
const multiplySpy = jest.spyOn(calculator, 'multiply')
.mockImplementation((a, b) => 0);
expect(calculator.multiply(5, 5)).toBe(0);
multiplySpy.mockRestore();
expect(calculator.multiply(5, 5)).toBe(25);
});
});
Mocking API Calls
// api.js
const axios = require('axios');
async function getUser(id) {
const response = await axios.get(`/api/users/${id}`);
return response.data;
}
async function createUser(userData) {
const response = await axios.post('/api/users', userData);
return response.data;
}
module.exports = { getUser, createUser };
// api.test.js
jest.mock('axios');
const axios = require('axios');
const { getUser, createUser } = require('./api');
describe('API module', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('fetches a user by id', async () => {
const mockUser = { id: 1, name: 'Alice' };
axios.get.mockResolvedValue({ data: mockUser });
const user = await getUser(1);
expect(axios.get).toHaveBeenCalledWith('/api/users/1');
expect(user).toEqual(mockUser);
});
it('creates a new user', async () => {
const newUser = { name: 'Bob', email: 'bob@example.com' };
const createdUser = { id: 2, ...newUser };
axios.post.mockResolvedValue({ data: createdUser });
const result = await createUser(newUser);
expect(axios.post).toHaveBeenCalledWith('/api/users', newUser);
expect(result.id).toBe(2);
});
it('handles API errors', async () => {
axios.get.mockRejectedValue(new Error('Network error'));
await expect(getUser(1)).rejects.toThrow('Network error');
});
});
Mocking Partial Modules
Mock only specific exports:
// utils.js
export function formatDate(date) { /* ... */ }
export function formatCurrency(amount) { /* ... */ }
export function calculateTax(amount) { /* ... */ }
// test.js
jest.mock('./utils', () => ({
...jest.requireActual('./utils'), // Keep real implementations
calculateTax: jest.fn(() => 10) // Mock only this one
}));
Try It: Mock This API
Loading Code Editor...
Clearing and Resetting Mocks
const mockFn = jest.fn(() => 'default');
beforeEach(() => {
// Clear call history but keep implementation
mockFn.mockClear();
// or jest.clearAllMocks();
});
afterEach(() => {
// Reset to initial state (clears calls AND implementation)
mockFn.mockReset();
// or jest.resetAllMocks();
});
afterAll(() => {
// Restore original implementation (for spies)
// jest.restoreAllMocks();
});
Differences:
| Method | Clears Calls | Resets Implementation |
|---|---|---|
mockClear() | Yes | No |
mockReset() | Yes | Yes (to undefined) |
mockRestore() | Yes | Yes (to original) |
Mock Matchers
Jest provides specific matchers for mocks:
const mockFn = jest.fn();
mockFn('first', 'call');
mockFn('second', 'call');
// Check if called
expect(mockFn).toHaveBeenCalled();
// Check call count
expect(mockFn).toHaveBeenCalledTimes(2);
// Check specific arguments
expect(mockFn).toHaveBeenCalledWith('first', 'call');
// Check last call
expect(mockFn).toHaveBeenLastCalledWith('second', 'call');
// Check nth call (1-indexed)
expect(mockFn).toHaveBeenNthCalledWith(1, 'first', 'call');
// Access call arguments directly
expect(mockFn.mock.calls[0]).toEqual(['first', 'call']);
expect(mockFn.mock.calls.length).toBe(2);
// Access return values
mockFn.mockReturnValue('result');
mockFn();
expect(mockFn.mock.results[0].value).toBe('result');
Key Takeaways
- Use
jest.fn()to create mock functions - Use
jest.mock()to mock entire modules - Use
jest.spyOn()to spy on existing methods - Mock return values with
mockReturnValue()andmockResolvedValue() - Clear mocks between tests with
jest.clearAllMocks() - Use
.mock.callsand.mock.resultsto inspect mock behavior - Mock dependencies, not the code you're testing
Next, we'll learn how to test React components with Jest and Testing Library!

