Automatic Mock
The first method we are implementing is Jest's Automatic Mock. You can import the module that you want to mock and call jest.mock().
Program
import SoundPlayer from './sound-player';
jest.mock('./ProductsClient');

You can also try this code with Online Javascript Compiler
Run Code
We can call jest.mock('./sound-player'), which will return a practical "automatic mock" that we use to spy on any calls to the class constructor and all of its methods. jest.mock replaces the ES6 class with a mock constructor and replaces all of its methods with mock functions that will always return undefined.
Jest save Method calls in theAutomaticMock.mock.instances[index].methodName.mock.calls.
That is the easiest option; you can use Automatic Mock in this example if you do not want to replace the implementation of the class.
Program
//sound-player-consumer.test.js
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
jest.mock('./sound-player'); // Now SoundPlayer is a mock constructor.
beforeEach(() => {
// Clears all instances and calls to the constructor and all its methods.
SoundPlayer.mockClear();
});
it('check if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it('check if the consumer called a method on the class instance', () => {
// Shows that mockClear() is working.
expect(SoundPlayer).not.toHaveBeenCalled();
const soundPlayerConsumer = new SoundPlayerConsumer();
// Constructor should have been called again.
expect(SoundPlayer).toHaveBeenCalledTimes(1);
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
// automatic mocks also has mock.instances .
const mockSoundPlayerInstance = SoundPlayer.mock.instances[0];
const mockPlaySoundFile = mockSoundPlayerInstance.playSoundFile;
expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName);
// Similar to the above check.
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
expect(mockPlaySoundFile).toHaveBeenCalledTimes(1);
});

You can also try this code with Online Javascript Compiler
Run Code
Output

Manual Mocks
We can also implement Manual Mocks in the __mocks__ folder. Mocking Class manually lets us specify the implementation of the class and use it across test files.
You can use jest.fn() to create a mock implementation that mock playing audio file in our SoundPlayerConsumer example.
Program
// __mocks__/sound_player.js
export const mockPlaySoundFile = jest.fn();
const mock = jest.fn().mockImplementation(() => {
return {playSoundFile: mockPlaySoundFile};
});
export default mock;
We can now import this mock and the mock methods shared by all instances in our test file.
Program
//sound-player-consumer.test.js
import SoundPlayer, {mockPlaySoundFile} from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
jest.mock('./sound-player'); // Now SoundPlayer is a mock constructor.
beforeEach(() => {
// Clears all instances and calls to constructor and all methods:
SoundPlayer.mockClear();
mockPlaySoundFile.mockClear();
});
it('checks if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it('checks if the consumer called a method on the class instance', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
});

You can also try this code with Online Javascript Compiler
Run Code
Output

Module Factory Parameter in jest.mock()
A module factory is a function that returns the mock when you take it as an argument as jest.mock(path, moduleFactory).
The module factory must return a constructor function to mock a constructor function.
In other words, the module factory must be a function that returns a function, i.e., a higher-order function (HOF). You can learn more about higher-order components by following the Higher-Order Components in React article.
Program
import SoundPlayer from './sound-player';
const mockPlaySoundFile = jest.fn();
jest.mock('./sound-player', () => {
return jest.fn().mockImplementation(() => {
return {playSoundFile: mockPlaySoundFile};
});
});

You can also try this code with Online Javascript Compiler
Run Code
There is a limitation with the factory parameter; since any calls to jest.mock() are hoisted to the top of a file, it is impossible to define a variable and then use it in the factory. Though jest has an exception for variables starting with the word 'mock,' you must make sure they initialize on time. For example, the code below throws an out-of-scope error because of the word 'fake' instead of 'mock' in the variable declaration.
Program
// Note: this will fail, it is only a demostration
import SoundPlayer from './sound-player';
const fakePlaySoundFile = jest.fn();
jest.mock('./sound-player', () => {
return jest.fn().mockImplementation(() => {
return {playSoundFile: fakePlaySoundFile};
});
});

You can also try this code with Online Javascript Compiler
Run Code
mockImplementation() or mockImplementationOnce()
Another way to change implementation is to replace the mocks in a test or all the tests by calling mockImplementation() on the existing mock.
Jest hoist the jest.mock calls to the top of the code, and we can specify a mock later. One way to do this is by calling mockImplementation() or mockImplementationOnce() on the existing mock instead of using the factory parameter inside beforeAll. That also allows us to change the mock between tests if required.
Program
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
jest.mock('./sound-player');
describe('If SoundPlayer throws an error', () => {
beforeAll(() => {
SoundPlayer.mockImplementation(() => {
return {
playSoundFile: () => {
throw new Error('Test error');
},
};
});
});
it('Should throw error when calling playSomethingCool', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(() => soundPlayerConsumer.playSomethingCool()).toThrow();
});
});

You can also try this code with Online Javascript Compiler
Run Code
Output

FAQs
-
How does Jest mock work?
Mock functions allow us to test links between our code by erasing the actual implementation, capturing calls to that function (and the parameters passed in those calls), capturing instances of constructor functions when they initialize with new, and allowing test-time configuration of the return values.
-
Is read only a Jest mock?
The way we import the module into the jest file mainly allows us to mock read-only functions. While you import '* as navigate,' you essentially get a copy of all of the named exports and default exports within that module and define it as navigate.
-
Where do you put Jest.mock()?
In Jest, mock calls to the top of the module (before any imports). Because calls to jest.mock() hoist to the top of the file; it is impossible to define a variable and then use it.
-
What is Jest spyOn?
jest.spyOn allows us to mock either the whole module or the individual functions of a module in jest. Most generally, we use spyOn to track calls on a method.
Key Takeaways
This article serves as an introduction to Mocking Class Methods in Jest. Throughout the example in this article, you have worked with all four methods to mock ES6 classes using jest. You can try other examples and test suites to further your learning of mock classes.
You may also learn more about the setup and scope of tests in jest here. Also, be sure to check the Modules article on Coding Ninjas and head over to our practice platform Coding Ninjas Studio to practice top problems, attempt mock tests, read interview experiences, and much more. Till then, Happy Learning!