I am working for a reducer test. But the return state from reducer with action function is abnormal.
reducer.react-test.js
import reducer from '../../test_module/reducer';
describe('Test Reducer', () => {
const initStatus = { id: -1, list: [] };
it('1. has default state', () => {
expect(reducer(initStatus, { type: 'unexpected' })).toEqual({
...initStatus
});
});
it('2. has added once', () => {
expect(reducer(initStatus, { type: "ADD" })).toEqual({
...initStatus,
id: 0,
list: [0],
});
});
it('3. has added twice', () => {
const afterAddOnce = reducer(initStatus, { type: "ADD" });
expect(reducer(afterAddOnce, { type: "ADD" })).toEqual({
...initStatus,
id: 1,
list: [0,1],
});
});
})
reducer.js
export default function reducer(state={
id: -1, list: [],
}, action) {
switch(action.type) {
case "ADD": {
state.id = state.id + 1;
state.list.push(state.id);
return {
...state,
};
}
}
return state;
}
folder structure
.
├── __test__
│ └── test_module
│ └── reducer.react-test.js
└── test_module
└── reducer.js
My first and second test case works expectedly.
However, while I tried to trigger action twice, I store the return state from first action. But the return state is unexpectedly, which executed ADD
action twice. (I expected once only)
Thus I got this result while I run jest
:
FAIL __test__/test_module/reducer.react-test.js
● Test Reducer › has added twice
expect(received).toEqual(expected)
Expected value to equal:
{"id": 1, "list": [0, 1]}
Received:
{"id": 2, "list": [0, 1, 2]}
Difference:
- Expected
+ Received
Object {
- "id": 1,
+ "id": 2,
"list": Array [
0,
1,
+ 2,
],
}
I must misunderstand the usage of trigging reducer function with action. I hope to get some proper way to trigger reducer with action and get expected result.
The culprit can be found in your reducer, specifically these lines:
state.id = state.id + 1;
state.list.push(state.id);
The reducer here is both mutating the state, and returning a new state. Mutating the state is bad practice, and can cause weird effects (like the one you're experiencing)
So the '2. has added once' test will increment the id to 0. This will stay in memory during the next test where initialStatus will have an id of 0, not -1. In our reducer, state points to initialStatus. So calling state.id = status.id + 1;
increments initialStatus's id field, and same for list. This is why it appears that the ADD action being called three times.
Moving away from mutations in the reducer can help fix this problem.
case "ADD": {
const newId = state.id + 1;
return {
id: newId,
list: state.list.concat([newId])
};
}
Remember, never mutate the state in a reducer, always return the new state instead! (http://redux.js.org/docs/Troubleshooting.html#never-mutate-reducer-arguments)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With