Action Creators and Reducers

Learn the changes required to tests when you change the project structure.

Usually, when writing unit tests, it is recommended to test each part of the system separately and only test connections and interfaces in the integration tests. In the case of Redux, though, it is worth considering the connection between action creators and reducers.

The way we’ve built our tests defines the ADD_RECIPE action object in three different places: the recipe’s action creators, the recipe’s tests, and the reducer’s tests.

If we decide to change the structure of the ADD_RECIPE action in the future, our action creator tests will catch the change and remind us to update the test code. But the reducer’s tests will continue to pass unless we remember to change the hardcoded ADD_RECIPE action objects used in those tests.

This can lead to painful edge cases where all the tests pass, but the system doesn’t work. To avoid this, we can stop using hardcoded action objects in reducers and rely on action creators directly:

//Reducer tests modified to use action creators directly
it('should add recipe to empty list', () => {
  expect(reducer(initialState, addRecipe('test'))).toMatchSnapshot()
});

it('should add recipe to non-empty list', () => {
  const baseState = deepFreeze(reducer(initialState, addRecipe('first')));

  expect(reducer(baseState, addRecipe('test'))).toMatchSnapshot();
});

While somewhat breaking the unit test principle, combining the reducers with action creators results in cleaner code, fewer bugs, and less duplication.

Get hands-on with 1200+ tech skills courses.