A simple and robust workflow for real life apps

Presented below is part two of our series about a simple and robust workflow for real life apps. If you haven’t read part one — “Redux Step by Step: A Simple and Robust Workflow for Real Life Apps” — it’s recommended you do so first.

Redux has become one of the most popular Flux implementations for managing data flow in React apps. Reading about Redux, though, often causes a sensory overload where you can’t see the forest for the trees. This also holds for testing Redux projects.

As usual, we’ll start by iterating that there isn’t one right way to test your Redux project. We will present an opinionated flavor that we personally believe in.

Some motivation

One of the biggest motivations for testing is engineering velocity. It may seem that writing tests slows down the development process, but this perceived notion only holds in the short term. Without automated tests, we’ve noticed that our projects can only grow that much before our ability to deliver grinds to a halt.

An automated suite of tests with full coverage is an incredibly powerful tool. It enables new contributors to push code without worry of breaking anything. It battles code rot by alleviating fear of drastic refactoring. It enables faster releases with more confidence. It allows to fix issues in production continuously without waiting for manual regression.

The pyramid of testing

What kind of tests should we be writing? Should we test the entire system together or every unit separately? Consider the following example:

If we were only testing the entire system together, we would need to write 25 tests for full coverage (5x5 flow combinations). A better approach is to mix tests from multiple levels: 5 unit tests for Module A by itself, 5 more unit tests for Module B by itself and 1–2 integration tests of the entire system together.

Applying the multi-level approach to the domain of testing front-end applications yields the famous pyramid of testing:

There’s a lot of ambiguity regarding what the pyramid looks like and the names of the different levels. This is our take:

E2E Tests — Testing the entire app from outside. Running on an actual browser or mobile device, with real servers, usually on production.

— Testing the entire app from outside. Running on an actual browser or mobile device, with real servers, usually on production. UI Automation Tests — Testing major modules of the app from outside. Running on an actual browser or mobile device, with mock servers.

— Testing major modules of the app from outside. Running on an actual browser or mobile device, with mock servers. Component Tests — Testing the UI and possibly its integration with business logic from inside. Usually running on Node, with mock services.

— Testing the UI and possibly its integration with business logic from inside. Usually running on Node, with mock services. Unit Tests — Testing business logic without UI, as separate units. Running on Node, mocking everything outside the unit. There’s also usually a layer of integration tests between units included here.

The different levels of tests have different characteristics. The higher you go up the pyramid, the more brittle and flaky the tests are. They provide more confidence, but are usually more expensive to write and maintain. They also run much slower. A good balance is having a lot of tests from the lower levels and fewer and fewer tests from the higher levels.

Unit tests for business logic

The remainder of this post will focus on the foundation of the pyramid of testing — unit tests for business logic. These are the majority of tests we expect to have in our project. We will focus on other levels in future posts.

Our Redux methodology enforces clear separation between components and business logic. Remember the rule: “Place all business logic inside action handlers (thunks), selectors and reducers.” This will allow us to ignore components altogether from this point forward.

We’ll continue the discussion over the Reddit app we’ve implemented in the previous post. It lets the user choose 3 topics from the front page subreddits and then see their posts in a filterable list. Refresh your memory with the code, it’s available here: https://github.com/wix/react-dataflow-example

Setting up for testing

Running unit tests requires some sort of test runner. Modern runners like jest include everything you need to test like setup and teardown, expectations and mocking.

Our app relies on create-react-app which already ships with jest pre-configured. You can run the following in terminal to set up:

Note that we’re also installing redux-testkit — a library that reduces the boilerplate for testing Redux and makes the process easier and more fun.

The last command npm test will start jest in watch mode and automatically run our tests as we’re writing them. If your project does not have jest pre-installed, you can install it easily by following these instructions or simply running npm install jest --save-dev

Our game plan

If we’ve been following the methodology, all of our business logic is found in the various Redux constructs like action handlers (thunks), selectors and reducers. All we have to do is practice how to test each one: