How Can I Mock A Service Within A React Component To Isolate Unit Tests In Jest?
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?"