Avoiding Mutations

Learn ways to avoid mutations while testing reducers.

One key requirement is that our reducers never modify the state but only create a new one. Our current tests do not verify this behavior.

While we can try to catch these mistakes by manually verifying that the initial state did not change, a simpler approach would be to “freeze” the initial state and have any changes to it automatically stop the tests. To achieve this, we can use the excellent deep-freeze library, installed as follows:

npm install deep-freeze --save

To use deep-freeze, we wrap our initial state with a call to deepFreeze():

import deepFreeze from 'deep-freeze';

const initialState = deepFreeze(reducer(undefined, { type: 'INIT' }));

Any attempt by any parts of our code to modify initialState will now automatically throw an error:

initialState.push('test');
> TypeError: Can't add property 0, object is not extensible

To ensure that our reducers never change the original state, we can always call deepFreeze() on the state before passing it on to the reducer:

// Updated adding to non-empty list test
it('should add recipe to non-empty list', () => {
  const initAction = { type: ADD_RECIPE, payload: 'first' };
  const baseState  = deepFreeze(reducer(initialState, initAction));

  expect(reducer(baseState, { type: ADD_RECIPE, payload: 'test' }))
    .toMatchSnapshot();
});

Get hands-on with 1200+ tech skills courses.