Mock Es6 classes using Jest
Update:
All this info and more has now been added to the Jest docs in a new guide, "ES6 Class Mocks."
Full disclosure: I wrote it. :-)
The key to mocking ES6 classes is knowing that an ES6 class is a function. Therefore, the mock must also be a function.
- Call
jest.mock('./mocked-class.js');
, and also import './mocked-class.js'. - For any class methods you want to track calls to, create a variable that points to a mock function, like this:
const mockedMethod = jest.fn();
. Use those in the next step. - Call
MockedClass.mockImplementation()
. Pass in an arrow function that returns an object containing any mocked methods, each set to its own mock function (created in step 2). - The same thing can be done using manual mocks (__mocks__ folder) to mock ES6 classes. In this case, the exported mock is created by calling
jest.fn().mockImplementation()
, with the same argument described in (3) above. This creates a mock function. In this case, you'll also need to export any mocked methods you want to spy on. - The same thing can be done by calling
jest.mock('mocked-class.js', factoryFunction)
, where factoryFunction is again the same argument passed in 3 and 4 above.
An example is worth a thousand words, so here's the code. Also, there's a repo demonstrating all of this, here: https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working
First, for your code
if you were to add the following setup code, your tests should pass:
const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want
Socket.mockImplementation(() => {
return {
connect: connectMock
};
});
(Note, in your code: Socket.mock.calls[0][1]
should be [0][0]
, and [0][2]
should be [0][1]
. )
Next, a contrived example
with some explanation inline.
mocked-class.js. Note, this code is never called during the test.
export default class MockedClass {
constructor() {
console.log('Constructed');
}
mockedMethod() {
console.log('Called mockedMethod');
}
}
mocked-class-consumer.js. This class creates an object using the mocked class. We want it to create a mocked version instead of the real thing.
import MockedClass from './mocked-class';
export default class MockedClassConsumer {
constructor() {
this.mockedClassInstance = new MockedClass('yo');
this.mockedClassInstance.mockedMethod('bro');
}
}
mocked-class-consumer.test.js - the test:
import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';
jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.
// console.log(MockedClass()); // logs 'undefined'
let mockedClassConsumer;
const mockedMethodImpl = jest.fn();
beforeAll(() => {
MockedClass.mockImplementation(() => {
// Replace the class-creation method with this mock version.
return {
mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
};
});
});
beforeEach(() => {
MockedClass.mockClear();
mockedMethodImpl.mockClear();
});
it('The MockedClassConsumer instance can be created', () => {
const mockedClassConsumer = new MockedClassConsumer();
// console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
expect(mockedClassConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
const mockedClassConsumer = new MockedClassConsumer();
expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});
it('We can check if the consumer called a method on the class instance', () => {
const mockedClassConsumer = new MockedClassConsumer();
expect(mockedMethodImpl).toHaveBeenCalledWith('bro');
// Checking for method call using the stored reference to the mock function
// It would be nice if there were a way to do this directly from MockedClass.mock
});