Table of contents
1.
Introduction
2.
Setup
2.1.
Program
2.2.
Program
3.
Automatic Mock
3.1.
Program
3.2.
Program
3.3.
Output
4.
Manual Mocks
4.1.
Program
4.2.
Output
5.
Module Factory Parameter in jest.mock()
5.1.
Program
5.2.
Program
6.
mockImplementation() or mockImplementationOnce()
6.1.
Program
7.
Output
8.
FAQs
9.
Key Takeaways
Last Updated: Mar 27, 2024

Jest Mock Classes

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Javascript classes act as a template for creating objects, and they encapsulate data with code to work on that data. We can use Jest to mock ES6 classes by importing them into the files we want to test. ES6 Classes formalize the typical JavaScript pattern that simulates class-like inheritance hierarchies using functions and prototypes. You can learn more about Objects in Jhere.

ES6 classes in JS are constructor functions with some syntactic sugar. Any mock for an ES6 class has to be a function or an actual ES6 class (which is also a function). That is why you can mock ES6 classes using mock functions.

There are four ways in which we can mock ES6 classes in jest. We will go through these four ways in this article with some examples.

If you are not familiar with the concept of Mocking in Jest, you can learn more about Mocking here

Setup

For examples that we will use in this article, we will set up an elementary ES6 class example. We will assume a SoundPlayer class that plays a sound file and a consumer class that uses that class, SoundPlayerConsumer. We are going to mock SoundPlayer in our tests for SoundPlayerConsumer.

The SoundPlayer class may look something like this:

Program

// sound-player.js
export default class SoundPlayer {
 constructor() {
   this.foo = 'bar';
 }

 playSoundFile(fileName) {
   console.log('Playing sound file ' + fileName);
 }
}
You can also try this code with Online Javascript Compiler
Run Code

 

And the SoundPlayerConsumer class could look something like this:

Program

//sound-player-consumer.js
import SoundPlayer from './sound-player';

export default class SoundPlayerConsumer {
 constructor() {
   this.soundPlayer = new SoundPlayer();
 }

 playSomethingCool() {
   const coolSoundFileName = 'song.mp3';
   this.soundPlayer.playSoundFile(coolSoundFileName);
 }
}
You can also try this code with Online Javascript Compiler
Run Code

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

  1. 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.
     
  2. 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.
     
  3. 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.
     
  4. 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!

Live masterclass