
Mocking in Jest
The goal for mocking is to replace something we don’t control with something we do.
The mock function provides feature to
- Capture calls
- Set return types
- Change implementation
Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with new, and allowing test-time configuration of return values.
There are 3 way main types of modules and function mocking in jest
- jest.fn: Mock a function
- jest.mock: Mock a module
- jest.spyOn: Spy or mock a function
Jest.fn()
The simplest way to create a mock function instance is with jest.fn()`
test("returns undefined by default", () => {
const mock = jest.fn();
let result = mock("foo"); expect(result).toBeUndefined();
expect(mock).toHaveBeenCalled();
expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith("foo");
});test('check history',() => {
var history = { push: jest.fn() } expect(history.push).toHaveBeenCalledWith('/product/2');
})
we can even change the return value, implementation, or promise resolution
test('mock implementation',() => {
const mock = jest.fn().mockImplementation(()=>'done mocking') expect(mock).toBe('done mocking')
mock.mockRestore();
});test("mock return value", () => {
const mock = jest.fn();
mock.mockReturnValue("bar"); expect(mock("foo")).toBe("bar");
expect(mock).toHaveBeenCalledWith("foo");
});
Jest provides a set of custom matchers to check expectations about how the function was called:
- expect(fn).toBeCalled()
- expect(fn).toBeCalledTimes(n)
- expect(fn).toBeCalledWith(arg1, arg2, …)
- expect(fn).lastCalledWith(arg1, arg2, …)
Jest.mock();
Jest can be used to mock ES6 classes that are imported into files you want to test.
The 4 ways to create an ES6 class mock
- Automatic mock
- Manual mock
- Replacing the mock using mockImplementation() or mockImplementationOnce()
- Calling jest.mock() with the module factory parameter
Automatic mocks
Calling jest.mock(‘…’) returns a useful “automatic mock” you can use to spy on calls to the class constructor and all of its methods
If you don’t need to replace the implementation of the class, this is the easiest option to set up. For example:
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';jest.mock('./sound-player'); //SoundPlayer is now a mock constructorbeforeEach(() => {
SoundPlayer.mockClear();
});it('We can check if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
})
Manual mocking
Create a manual mock by saving a mock implementation in the __mocks__ folder. This allows you to specify the implementation, and it can be used across test files.
Replacing the mock using mockImplementation() or mockImplementationOnce()
You can replace all of the above mocks in order to change the implementation, for a single test or all tests, by calling mockImplementation() on the existing mock.
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';jest.mock('./sound-player');
describe('When SoundPlayer throws an error', () => { beforeAll(() => {
SoundPlayer.mockImplementation(() => {
return {
playSoundFile: () => {
throw new Error('Test error');
},
};
});
}); it('Should throw an error when calling playSomethingCool', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(() => soundPlayerConsumer.playSomethingCool()).toThrow();
});
});
Jest.SpyOn();
Sometimes you only want to watch a method be called, but keep the original implementation. Other times you may want to mock the implementation, but restore the original later in the suite.
jest.spyOn allows you to mock either the whole module or the individual functions of the module.
An important thing to remember about jest.spyOn() will create a mock function that similar to jest.fn.
import * as productActions from 'src/actions/product';test(‘calls checkProduct', () => { jest.spyOn(productActions,'checkProduct')
.mockReturnValue(Promise.resolve());expect(productActions.checkProduct)
.toHaveBeenCalledWith(productId);
productActions.checkForDigital.mockRestore();
});