Beyond a single fixed value, mock functions can simulate conditional responses, errors, and behavior that depends on the arguments passed in. This lesson covers those more advanced return value patterns.
Returning Different Values Based on Arguments
Sometimes a fixed return value isn't enough — you need the mock to behave differently depending on what it was called with. .mockImplementation() accepts a full function, so you can branch on the arguments just like real code would.
This is especially useful for mocking a lookup function (like a repository's findById) where different IDs should return different fake records, or an error for an ID that shouldn't exist.
const findUser = jest.fn((id) => {
if (id === 1) return { id: 1, name: 'Ada' };
if (id === 2) return { id: 2, name: 'Grace' };
return null;
});
expect(findUser(1)).toEqual({ id: 1, name: 'Ada' });
expect(findUser(999)).toBeNull();
The mock's implementation branches on the argument, just like a real (simplified) lookup function would.
Simulating Errors and Async Failures
mockFn.mockImplementation(() => {
throw new Error('Something went wrong');
});
mockFn.mockRejectedValueOnce(new Error('Network timeout'));
A .mockImplementation() that throws simulates a synchronous function failing.
.mockRejectedValue()/.mockRejectedValueOnce() simulate an async function failing.
Combine .mockImplementationOnce() calls to simulate "fails once, then succeeds" retry scenarios.
Always test both the success path and at least one realistic failure path.
Mock Return Value Patterns Cheat Sheet
Common advanced patterns for configuring mock behavior.
Pattern
Example
Fixed value
mockFn.mockReturnValue(42)
Sequenced values
.mockReturnValueOnce(1).mockReturnValueOnce(2)
Argument-based logic
jest.fn((id) => (id === 1 ? userA : userB))
Simulated error
.mockImplementation(() => { throw new Error('x'); })
Async success
.mockResolvedValue({ ok: true })
Async failure
.mockRejectedValue(new Error('x'))
Fail once, then succeed
.mockRejectedValueOnce(err).mockResolvedValue(ok)
Testing Retry Logic with Sequenced Mocks
Retry logic is a great candidate for mock sequencing: configure the mock to fail on the first call and succeed on the second, then verify your retry wrapper eventually returns the successful result.
const fetchData = jest.fn()
.mockRejectedValueOnce(new Error('Timeout'))
.mockResolvedValueOnce({ data: 'ok' });
test('retries once after a failure', async () => {
const result = await fetchWithRetry(fetchData, { retries: 1 });
expect(result).toEqual({ data: 'ok' });
expect(fetchData).toHaveBeenCalledTimes(2);
});
Resetting Mock Configuration Between Tests
Because mock implementations and return values persist across tests unless cleared, use jest.clearAllMocks() (clears calls) or jest.resetAllMocks() (also clears implementations) in afterEach() or beforeEach() to avoid one test's configuration leaking into the next.
afterEach(() => {
jest.resetAllMocks(); // clears calls AND any mockReturnValue/mockImplementation
});
Common Mistakes
Assuming jest.clearAllMocks() also resets .mockReturnValue()/.mockImplementation() configuration (it doesn't; use resetAllMocks()).
Hardcoding a single return value when the function under test calls the mock multiple times with different meaningful arguments.
Never testing the failure/error path, only the happy path with a successful mock response.
Forgetting .mockReturnValueOnce() values are consumed in call order, not by matching arguments.
Key Takeaways
.mockImplementation() can branch on arguments to simulate realistic conditional behavior.
Errors and async rejections can be simulated directly with .mockImplementation()/.mockRejectedValue().
Sequenced .mockImplementationOnce()/.mockRejectedValueOnce() calls are ideal for testing retries.
Use jest.resetAllMocks() to clear both call history and configured behavior between tests.
Pro Tip
When writing retry or fallback logic, always write a test where the mock fails on every call — it's the scenario most likely to reveal an infinite loop or unhandled rejection bug.
You now know advanced mock return value patterns. Next, learn how to mock entire modules with jest.mock().