I have an ES6 class which I need to mock it's methods. Following the documentation i made a manual mock of this, and got the constructor to both be called and asserted.
My function that consumes this class is just a basic function that runs one of the class methods.
test.js
const mockConnect = jest.fn();
const mockAccess = jest.fn();
jest.mock('../../src/connection');
const connection = require('../../src/connection').default;
connection.mockImplementation(() => {
return {
connect: mockConnect,
access: mockAccess.mockReturnValue(true),
};
});
caller_function();
expect(connection).toHaveBeenCalled(); // works properly as the constructor is called
expect(connection).toHaveBeenCalledWith('something'); // works
expect(mockAccess).toHaveBeenCalled(); // says it was not called when it should have
caller_function.js
import connection from 'connection';
const conn = new connection('something');
export function caller_function() {
conn.access(); // returns undefined when mock says it should return true
}
This is happening because you're using mockImplementation()
instead of a manual mock or the factory parameter to jest.mock()
, and your mocked object is being created during the module loading process, since the constructor call is not inside of any function. What's happening is:
- The call to
jest.mock('../../src/connection')
runs and sets connection
to be an automatic mock.
- The
conn
object is created using the automatic mock. Therefore its access
method returns undefined.
- The call to
mockImplementation()
happens, changing the connection
mock. However, since the conn
object has already been created, it doesn't get the custom implementation.
Moving the constructor call into caller_function
is one way to fix it:
export function caller_function() {
const conn = new connection('something');
conn.access();
}
You could also use the factory parameter to jest.mock()
, specifying the implementation there, instead of calling mockImplementation()
. That way you won't have to change your implementation code:
const mockConnect = jest.fn();
const mockAccess = jest.fn();
import connection from '../../src/connection';
jest.mock('./so-import', () => {
return jest.fn().mockImplementation(() => {
return {
connect: mockConnect,
access: mockAccess.mockReturnValue(true)
};
});
});
...
BTW the convention for ES6 class names is to begin with an uppercase letter. I was temporarily confused by the lowercase name connection
.
Did you try doing connection.mockClear();
before you write a mockImplementation for the methods?
Also please refer to this https://jestjs.io/docs/en/es6-class-mocks