Redux-api-call's makeFetchAction
comes with its own dataSelector
, but in real world, we can't just grab and use it.
We may need to write our selector
which bases on dataSelector
.
Then when we write our unit test of that selector, we have to setup state
which has api_calls
property and the response.
For example:
const state = {
// some other properties
api_calls: {
fooApi: {
// meta data of this api call
data: {
foo: 'bar'
}
}
}
}
Number 1: Setting up this state is very tedious task for us because what we really want to test is just selector
of our response (
which is { foo: 'bar' }
in this case, but we end up making (almost) the whole state
object.
Number 2: We want to write test for our selector
, not makeFetchAction's selector
.
How can we do better? Imagine that we have something like this API to construct our state
base on actionCreator
from makeFetchAction
import setAPIResponse from './testUtils/setAPIResponse';
const state = setAPIResponse(actionCreator, {
foo: "bar"
}).withState({});
The state
will be exactly the same with the state above. Want to test selector which selects from more than 1 selector
, here
is your recipe.
import setAPIResponse from './testUtils/setAPIResponse';
const state = setAPIResponse(actionCreator, {
foo: "bar"
})
.setAPIResponse(anotherActionCreator, {
duck: {
age: 27,
name: 'Melon'
}
})
.withState({});
You know what I mean, right? And you can also pass the inititalState
or seedState
along with it by calling withState
.
Here is the implemetation of the setAPIResponse
.
import { get } from 'lodash';
/* eslint-disable */
class MockAPIResponse {
constructor(state = {}) {
this.state = state;
}
setAPIResponse = (actionCreator, json) => {
const action = actionCreator();
const symbol = Object.getOwnPropertySymbols(action)[0];
// eslint-disable-next-line immutable/no-let
let apiName;
if (symbol) {
apiName = action[symbol].name;
} else {
apiName = action[0].payload.name;
}
this.val = {
[apiName]: {
data: json,
},
};
return new MockAPIResponse(this.withState(this.state));
}
withState = (state) => {
return {
...this.state,
...state,
api_calls: {
...state.api_calls,
...this.state.api_calls,
...get(state, 'api_calls', {}),
...this.val,
},
};
}
}
export default new MockAPIResponse().setAPIResponse;
Good luck and happy testing.