Just a quick refresher on what reducer is before we go into testing and code. Redux documentation is still great, in fact it covers unit tests really well you don’t even have to read this post. To summarize it, the reducer is a pure function that takes the previous state and an action, and returns the next state.
If you’re interested to learn how to test async redux actions, checkout
Testing async redux actions with Jest
Testing your code is important. But in my experience writing tests is one of the lowest priorities, it’s always about…
Because it’s a pure function, it’s easy to test. It’s a function that produces no side effects; given the same input, will always return the same output; doesn’t rely on external state.
What to test
Usually reducer consists of initial state and switch statement to handle every action. I like to break down my store into different sub-stores and have separate reducers for each sub-store. Sometimes one switch/case may handle few actions, because the business logic is the same and outcome should be the same. Some example GET_POST and UPDATE_POST should update the same store and produce same outcome.
It’s important to test reducers. That’s where business logic should happen and where new application state is formed based on external (API) or internal response.
As any unit test, it starts with boilerplate setup and writing empty tests just to outline what needs to be tested. I like to to test the initial state and then every switch/case in the reducer to see if action.payload will produce expected store. If you stop and think about it — it’s simple.
Add tests and mock data
From here, we can just start filling in the tests one by one. I also like to create mock files and store the API response at the time of testing. So I can test agains “real” data. This strategy uncovered few accidental API changes where the response was changed without any warning. Instead of blaming anyone, we can just look at the mock data and see what’s expected on the front end.
Having unit tests for all reducers will prevent any issues related to global application state. It’s especially useful if there are a lot of different API calls and every call will modify a state. It’s easy to break entire application state with a small refactoring without realizing it.