Spy
A spy allows you to watch a real function and track its calls without changing its implementation.
import * as math from './math';
test('spy example', () => {
const spy = jest.spyOn(math, 'add');
math.add(2, 3);
expect(spy).toHaveBeenCalledWith(2, 3);
expect(spy).toHaveBeenCalledTimes(1);
spy.mockRestore();
}); Mock
Mocking involves creating a fake object that simulates the behavior of a real object.
import * as math from './math';
test('mock example', () => {
jest.spyOn(math, 'add').mockImplementation((a, b) => 100);
const result = math.add(2, 3);
expect(result).toBe(100);
expect(math.add).toHaveBeenCalledWith(2, 3);
}); Stub
Stubs typically just replace a method with a fixed return value.
import * as math from './math';
test('stubbing math.add', () => {
math.add = jest.fn(() => 42);
const result = math.add(5, 3);
expect(result).toBe(42);
expect(math.add).toHaveBeenCalledWith(5, 3);
expect(math.add).toHaveBeenCalledTimes(1);
}); When to use what?
- If you’re unit testing a function that uses
math.add, stubbing might be the best choice for its simplicity. - If you’re doing integration testing and want to ensure
math.addis called correctly without changing its behavior, spying would be better. - Full mocking, why do we even need it?