Byrd
Byrd

Reputation: 897

ES6 class jest mocking

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
}

Upvotes: 1

Views: 1501

Answers (2)

stone
stone

Reputation: 8652

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:

  1. The call to jest.mock('../../src/connection') runs and sets connection to be an automatic mock.
  2. The conn object is created using the automatic mock. Therefore its access method returns undefined.
  3. 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.

Upvotes: 1

Pooja Gurav
Pooja Gurav

Reputation: 463

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

Upvotes: 0

Related Questions