Redux is a popular pattern for helping with complex single page applications, allowing a clean separation between state, actions on that state, and how that state is rendered in a UI.

But, of course, nothing is for free there is a down-side to Redux. Although the core principles of Redux are simple and straight forward, they are often hidden behind helper functions which can make applications complicated and hard to validate. These helper functions tend to hide what is actually going on and the developer loses some of the code transparency, which can make it easier for bugs to creep in as the code evolves or new code paths created which rarely get triggered (e.g. error handling).

So, how can you quickly test and validate your Redux classes without needing to stand up an instance of your application and run live tests using tools such as Selenium? (Nothing wrong with Selenium — I’m a fan — but I want to use the right tool for the right job and for this we need something much lighter-weight).

The answer for us was a unit testing framework called Jest. Jest allows us to define tests and to mock classes which provide the underlying XHR calls such that we can run our tests completely standalone outside of any browser in seconds, all as part of our CI/CD pipeline. As an added bonus it also does a decent job of reporting code coverage, allowing developers to find those code branches that don’t get run (which are usually the ones which cause problems).

Having unit tests for our Redux classes not only helps to test code before it gets delivered, but it also acts as a permanent regression suite preventing innocuous changes from introducing bugs. We have also found that it’s a useful reference point for describing how a Redux class should function.

Data loader example

To demonstrate how we have tested and mocked our Redux classes, let’s take an example. The DataLoader class abstracts some of the complexity of calling various REST endpoints and updating a UI. The DataLoader class has the following state:

It has the following actions and reducers defined to manipulate the state:

The DataLoader class also uses action side-effects to perform XHR calls, which upon success or failure will then invoke further actions:

This side-effect code can be impenetrable at first (and second..) glance, but it’s essentially saying that after the REQUEST_DATA action is applied invoke the local function _createDataRequest(), and after the SET_DATA_LOADING action is applied invoke the local function _getData(). These functions make the required XHR calls (via the wrapper DataLoaderService) and return an Observable which is used to trigger another action upon success or failure:

The final piece of the DataLoader class is a set of selectors which allow the state to be inspected:

With this all in place, there are several areas we can unit test:

Action creators

creators Reducers state manipulation

state manipulation Selectors returning correct values

returning correct values At a slightly higher level, thinking about action side effects from the Epic, there are several possible action paths through this DataLoader class which help us define the appropriate unit tests.

Happy path: REQUEST_DATA to SET_DATA_LOADING to SET_DATA_AVAILABLE. Error path: REQUEST_DATA (XHR fails) to SET_ERROR. Error path: REQUEST_DATA to SET_DATA_LOADING (XHR fails) to SET_ERROR.

(In all cases, the CLOSE action is applied as required to do a clean up).

Action creator unit tests

The simplest piece to unit test are the action creator functions which are generated by the helper classes. These require no mocking and can be unit tested using a simple describe…test…expect pattern from Jest. We add these, not only to check for typos but to also help understanding of what the Redux helper classes are doing. For example:

Reducer unit tests

Like the action unit tests, the reducer unit tests require no mocking and can be unit tested using a simple describe…test…expect pattern from Jest. For example:

Selector unit tests

Selector unit tests also require no mocking and can be unit tested using a simple describe…test…expect pattern from Jest. For example:

Action Side-Effects from the Epic

Action side-effects are more complicated to unit test as they require a dummy Redux store to be created, as well as mocking of the underlying XHR calls to simulate success or failure.

The creation of the dummy store is reasonably straight forward. We create a global function to do the creation, and then call that using the beforeEach() callback invoked automatically by Jest prior to each test:

Once the dummy store has been created, the unit tests can cover each success and failure scenario. Importantly the underlying DataLoaderService class is mocked using Jest so we can simulate success and failure responses from it, rather than requiring a real XHR call. An example success scenario unit test is:

In a similar way, an example of a failure unit test is:

Putting it all together

As the unit test is being developed, it can be run in insolation by using the Jest CLI, passing in the file name of the test(s) to run. Omitting this will run all tests found:

jest dataloader.test.js --coverage

The --coverage flag means that Jest will also report coverage of the files which get loaded:

Although I wouldn’t encourage an obsession in obtaining 100% code coverage, it’s reassuring that all the code we’ve written in our Redux class now has test coverage. As with all unit testing it’s painful to get started but once you begin it pays dividends in terms of the quality of what you’re producing.