Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS: how to trigger reducer with action in Jest

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.

like image 426
Kir Chou Avatar asked Oct 18 '22 13:10

Kir Chou


1 Answers

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)

like image 117
dpaulus Avatar answered Nov 03 '22 08:11

dpaulus