Simplifying Basic Reducer Test

Simplify the test that we wrote in the previous lesson.

Using the method of writing tests described in the previous lesson, we will need to find each test definition’s initial state and add more properties. This complicates the test writer’s job without providing any benefits. Luckily, the reducer already creates non-empty states. Since we already tested adding to an empty list in the first test, we can rely on our reducer to create a non-empty list with all the required recipe information. You pass an empty array into the reducer and it fills it with the relevant state:

// Building initial state using the reducer
const initialState = reducer([], { type: ADD_RECIPE, payload: 'first' });

This only partially solves the problem, as we are still treating the initial state as an empty array ([]). While this is true in our test case, other reducers might have to deal with more complicated structures. A simple solution would be to create a const initialState = [] at the root of the tests and rely on it when needed:

// Setting initial state for all tests
describe('reducers/recipes', () => {
  const initialState = [];

  it('should add recipe to empty list', () => {
    const action   = { type: ADD_RECIPE, payload: 'test' };
    const expected = [{ title: "test" }];
    const actual   = reducer(initialState, action);

    expect(actual).toEqual(expected);
  });

  it('should add recipe to non-empty list', () => {
    const testState = reducer(initialState,
        { type: ADD_RECIPE, payload: 'first' });
    const action   = { type: ADD_RECIPE, payload: 'test' };
    const expected = [{ title: "first" }, { title: "test" }];
    const actual   = reducer(testState, action);

    expect(actual).toEqual(expected);
  });
};

Although we use the same initialState in every test, it is still hardcoded in our test file. If our reducer changes the way the state is built, we will update the test files accordingly. To remove this dependency, we can rely on a feature enforced by Redux’s combineReducers(). It mandates that any reducer called with an undefined state must return its part of the initial state structure:

// Excerpt from our reducer
const initialState = [];

const reducer = (state = initialState, action) => {
  ...
};

This means we can use the reducer to get the initial state to use for all of our tests by calling it with undefined and any action:

// Generating the initial state using our reducer
const initialState = reducer(undefined, { type: 'INIT' });

The result will put the same [] in the initial state, but now any changes to what the reducer considers to be the initial state will be automatically picked up by the tests.

Get hands-on with 1200+ tech skills courses.