Skip to content Skip to sidebar Skip to footer

How Can I Mock A Service Within A React Component To Isolate Unit Tests In Jest?

I'm trying to refactor a unit test to isolate a service that calls an API using axios from the component calling the service. The service is for the moment really simple: import ax

Solution 1:

The problem with mocking class instances is that it may be difficult to reach class instance and its methods without having a reference. Since someService is local to component module, it can't be accessed directly.

Without specific mock, jest.mock('./SomeService') relies on class automatic mock that works in unspecified ways. The question shows that different instances of mocked class have different getObjects mocked methods that don't affect each other, despite getObjects is prototype method and conforms to new SomeService().getObjects === new SomeService().getObjects in unmocked class.

The solution is to not rely on automatic mocking but make it work the way it's expected. A practical way to make mocked method accessible outside class instance is to carry it alongside mocked module. This way mockGetObjects.mockImplementationOnce will affect existing someService. mockImplementationOnce implies that the method can change the implementation later per test:

import { mockGetObjects }, SomeService from './SomeService';

jest.mock('./SomeService', () => {
  let mockGetObjects = jest.fn();
  return {
    __esModule: true,
    mockGetObjects,
    default: jest.fn(() => ({ getObjects: mockGetObjects }))
  };
});

...

mockGetObjects.mockImplementationOnce(...);
// instantiate the component

If the method should have constant mocked implementation, this simplifies the task because the implementation can be specified in jest.mock. It may still be beneficial to expose mockGetObjects for assertions.


Solution 2:

After some trial and error playing around with different approaches suggested in the jest documentation, the only thing that seemed to work was calling jest.mock() with the module factory parameter, like so:

// rename data to start with 'mock' so that the factory can use it
const mock_data = {
  data: require('./test_json/object_list_response.json'),
};

jest.mock('./SomeService', () => {
  return jest.fn().mockImplementation(() => {
    return {
      getObjects: () => {
        return Promise.resolve(mock_data).then(response => response.data)
      }
    };
  });
});

// write tests

Using mockResolvedValue() didn't work because I couldn't chain .then() off of it.

If this leads anyone to a more elegant or idiomatic solution, I'd welcome other answers.


Solution 3:

For posterity, another solution is creating a manual mock within a __mocks__ folder (inspired by Estus Flask's comment and this documentation).

./__mocks__/SomeService.js

export const mockGetObjects = jest.fn()

const mock = jest.fn(() => {
    return {getObjects: mockGetObjects}
})

export default mock

Then the plain jest.mock('./SomeService') call works with the implementation later being defined in the test:

mockGetObjects.mockImplementationOnce(() => {
  return Promise.resolve(object_data).then(response => response.data)
})

Post a Comment for "How Can I Mock A Service Within A React Component To Isolate Unit Tests In Jest?"