How does jest.spyOn() work?
A spy must be attached to a method that already exists on an object. Jest wraps that method so it can record what happens when your application calls it.
The object still owns the method. That is why the syntax requires both the object and the name of the method.
Creating a spy does not stop the original function from running. The original implementation is called unless you explicitly mock it.
const calculator = {
add(a, b) {
return a + b;
}
};
const spy = jest.spyOn(calculator, 'add');
calculator.add(2, 3);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(2, 3);
What does jest.spyOn() record?
After the method runs, Jest stores information about each call. You can then use Jest matchers to verify what happened.
- Number of times the method was called.
- Arguments passed to every call.
- Order of the calls.
- Returned values.
- Whether a call returned or threw an error.
const spy = jest.spyOn(api, 'send');
api.send('first');
api.send('second');
expect(spy).toHaveBeenCalledTimes(2);
expect(spy)
.toHaveBeenNthCalledWith(1, 'first');
expect(spy)
.toHaveBeenNthCalledWith(2, 'second');
Mock a return value
Use mockReturnValue() when you want the method to return a predictable value instead of running its real implementation.
const spy = jest
.spyOn(userService, 'getName')
.mockReturnValue('Test User');
expect(userService.getName())
.toBe('Test User');
expect(spy)
.toHaveBeenCalledTimes(1);
Change behavior with mockImplementation()
A fixed value is not always enough. Use mockImplementation() when the mocked method needs custom logic.
const spy = jest
.spyOn(math, 'multiply')
.mockImplementation((a, b) => {
return 100;
});
expect(math.multiply(4, 5))
.toBe(100);
expect(spy)
.toHaveBeenCalledWith(4, 5);
Spy on async methods
For Promise-based functions, Jest provides mockResolvedValue() and mockRejectedValue() .
const spy = jest
.spyOn(api, 'fetchUser')
.mockResolvedValue({
id: 1,
name: 'Alex'
});
await expect(api.fetchUser())
.resolves
.toEqual({
id: 1,
name: 'Alex'
});
Restore the original method
If you changed the implementation of a spy, restore it after the test. This prevents mocked behavior from affecting another test.
const spy = jest
.spyOn(service, 'getValue')
.mockReturnValue('mocked');
expect(service.getValue())
.toBe('mocked');
spy.mockRestore();
Common jest.spyOn() errors
Most spy problems are caused by the target object, module export shape or the point at which the spy was created.
The method is not available on the object passed to jest.spyOn(), or you imported the module in a different shape.
The property cannot be replaced normally. This commonly appears with some module exports and requires a different mocking approach.
This is normal behavior for jest.spyOn(). Add a mock implementation when the real method should not execute.
Check that the spy was created before the method was called and that the application is using the same object you spied on.
jest.spyOn() vs jest.fn()
Both create Jest mock functions, but they start from different situations.