Is there a way to properly mock Reselect selectors for unit testing?
You could achieve this by mocking the entire selectors1.js
module, but also importing is inside test, in order to have access over the mocked functionality
Assuming your selectors1.js
looks like
import { createSelector } from 'reselect';
// selector
const getFoo = state => state.foo;
// reselect function
export const baseSelector = createSelector(
[getFoo],
foo => foo
);
and selectors2.js
looks like
import { createSelector } from 'reselect';
import selectors1 from './selectors1';
export const targetSelector = createSelector(
[selectors1.baseSelector],
foo => {
return foo.a;
}
);
Then you could write some test like
import { baseSelector } from './selectors1';
import { targetSelector } from './selectors2';
// This mocking call will be hoisted to the top (before import)
jest.mock('./selectors1', () => ({
baseSelector: jest.fn()
}));
describe('selectors', () => {
test('foo.a = 1', () => {
const state = {
foo: {
a: 1
}
};
baseSelector.mockReturnValue({ a: 1 });
expect(targetSelector(state)).toBe(1);
});
test('foo.a = 2', () => {
const state = {
foo: {
a: 1
}
};
baseSelector.mockReturnValue({ a: 2 });
expect(targetSelector(state)).toBe(2);
});
});
jest.mock
function call will be hoisted to the top of the module to mock the selectors1.js
module
When you import
/require
selectors1.js
, you will get the mocked version of baseSelector
which you can control its behaviour before running the test
The thing is that Reselect is based on the composition concept. So you create one selector from many others. What really you need to test is not the whole selector, but the last function which do the job. If not, the tests will duplicate each other, as if you have tests for selector1, and selector1 is used in selector2, then automatically you test both of them in selector2 tests.
In order to achieve:
- less mocks
- no need to specially mock result of composed selectors
- no test duplication
test only the result function of the selector. It is accessible by selector.resultFunc
.
So for example:
const selector2 = createSelector(selector1, (data) => ...);
// tests
const actual = selector2.resultFunc([returnOfSelector1Mock]);
const expected = [what we expect];
expect(actual).toEqual(expected)
Summary
Instead of testing the whole composition, and duplicating the same assertion, or mocking specific selectors outputs, we test the function which defines our selector, so the last argument in createSelector, accessible by resultFunc
key.