How to use spyOn effectively
Spying on functions while testing allow us to verify that methods or components behave as expected. When spying, we can intercept and check the execution of functions. This is really useful for testing a module without affecting other named exports.
While mocking replaces the function you want to test with a new one (or a fake one), spying maintain the original function in order to observe their behavior.
Spying named exports
You have to spy the file containing the function, and then indicate which of the methods that are part of the file to spy on. Example:
function sum(a, b) {
return a + b;
}
export { sum }
import * as sumModule from './sum';
test('sum function has received 1 + 2', () => {
const spy = jest.spyOn(sumModule, 'sum');
sumModule.sum(1, 2);
expect(spy).toHaveBeenCalledWith(1, 2);
spy.mockRestore();
});
Why import * as?
Named exports must be spied on as properties of the module object, not as destructured values. For instance, if you do
import { sum } from './sum';
you get a copy of the function, but not the reference of the module’s property. Because of that, jest.spyOn() won’t work!
Spying default exports
Using the same example as before (but using a default export for the function)
export default function sum(a, b) {
return a + b;
}
we have to import still the whole module as an object, but now, spy on the default property.
import * as sumModule from './sum';
test('sum function has received 1 + 2', () => {
const spy = jest.spyOn(sumModule, 'default');
sumModule.default(1, 2);
expect(spy).toHaveBeenCalledWith(1, 2);
spy.mockRestore();
});
The default export is available as sumModule.default, not as sumModule.sum!
Note:
Always callspy.mockRestore()after your test to clean up and avoid side effects in other tests.