Redux Testing

Andrew Bliss
2 min readJul 24, 2018

Testing your code is essential in todays world. You must ensure that you keep your state powerful, consistent, and understandable. Redux is framework agnostic so writing tests for it is super easy.

Actions

Actions define all the ways your state can change. Everything the user can perform an action on will be defined.

contstants/index.jsexport const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const ADD_COUNTER = 'ADD_COUNTER';
export const REMOVE_COUNTER = 'REMOVE_COUNTER';
actions/index.jsimport * as types from '../constants';export function Increment(index) {
return dispatch => {
dispatch({
type: types.INCREMENT,
index,
});
};
}
export function Decrement(index) {
return dispatch => {
dispatch({
type: types.DECREMENT,
index,
});
};
}
export function AddCounter() {
return dispatch => {
dispatch({
type: types.ADD_COUNTER,
});
};
}
export function RemoveCounter(index) {
return dispatch => {
dispatch({
type: types.REMOVE_COUNTER,
index,
});
};
}

Reducers

Reducers define how actions and their payloads will be interpreted.

reducers/Counters.jsimport * as types from '../constants';const CountersReducer = (state = [0], action) => {
switch (action.type) {
case types.INCREMENT:
return [
...state.slice(0, action.index),
state[action.index] + 1,
...state.slice(action.index + 1),
];
case types.DECREMENT:
return [
...state.slice(0, action.index),
state[action.index] - 1,
...state.slice(action.index + 1),
];
case types.ADD_COUNTER:
return [...state, 0];
case types.REMOVE_COUNTER:
return [
...state.slice(0, action.index),
...state.slice(action.index + 1),
];
default:
return state;
}
};
export default CountersReducer;

This reducer has four actions that be performed. It can increment or decrement a single counter, and it can remove counters from the state.

Tests

Testing is one of the most important parts of writing code. You must ensure your code is reliable.

import CountersReducer from '../reducers/Counter';
import * as types from '../../constants';
import deepFreeze from 'deep-freeze';
describe('Counters Reducer', () => { it('should return the initial state', () => {
expect(CountersReducer(undefined, {}))
.toEqual([0]);
});
it('should increment the counter', () => {
const beforeState = [0];
const afterState = [1];
const action = { type: types.INCREMENT, index: 0 };
deepFreeze(beforeState);
expect(CountersReducer(beforeState, action))
.toEqual(afterState);
});
it('should decrement the counter', () => {
const beforeState = [0];
const afterState = [-1];
const action = { type: types.DECREMENT, index: 0 };
deepFreeze(beforeState);
expect(CountersReducer(beforeState, action))
.toEqual(afterState);
});
it('should add a counter', () => {
const beforeState = [0];
const afterState = [0, 0];
const action = { type: types.ADD_COUNTER };
deepFreeze(beforeState);
expect(CountersReducer(beforeState, action))
.toEqual(afterState);
});
it('should remove a counter', () => {
const beforeState = [0, 0];
const afterState = [0];
const index = 0;
const action = { type: types.REMOVE_COUNTER, index };
deepFreeze(beforeState);
expect(CountersReducer(beforeState, action))
.toEqual(afterState);
});
});

First begin with testing the initial state of the reducer to equal an array with the first counter [0]. The next tests increments the first counter by one and after decrements the first counter. Then the final tests add and remove a counter to the array.

Conclusion

Testing Redux is fairly simple and straightforward. There are some more complex situations and asynchronous behavior that might get tricky, but in the end your app with thank you.

--

--