Newer Post

React Baby Steps V: Is React Native for You?

Older Post

React Baby Steps III: Idiomatic Redux

React Baby Steps IV: Testing React-Redux

Everything should be made as simple as possible, but not simpler.
~ Albert Einstein

I prefer to apply the KISS Principle when solving problems, so the same goes for testing React-Redux apps.

I am building this testing piece upon my Idiomatic Redux article and app. Here, you can find the React Countries app with tests included that I will be referring to. After spending some time reading, thinking, and trying different approaches, I came up with a relatively simple, but efficient testing strategy. My goal was not to have a near 100% test coverage but rather to test what really matters without using too many resources (time, energy) and complex solutions.

The App

The testing described in this article works with the following app setup/configuration:

  • React 16
  • create-react-app
  • Redux
  • Thunk middleware

Nevertheless, I am sure it can be adjusted for other setups.

What (Not) to Test

This is one of the most important questions – what to test and, more importantly, what not to test. My approach is to focus mostly on unit testing and some functional testing. Integration, E2E, and complex interaction testing is completely skipped and left for the QA team 😛

We will test:

  • Dumb/Presentational React components – a minimalistic approach
  • Reducers
  • Thunk action creators
  • Util/Common code

We will NOT test:

  • Smart/Connected components
  • Simple action creators
  • Interaction between components
  • Integration and E2E

The reasoning behind this decision is the following:

  • We just want to make sure that presentational components actually render without exploding and test some simple things like: is a checkbox initially checked or are there 3 li elements if I pass in an array with 3 countries, etc.
  • Smart connected components require a redux store, either mocked or a real one, which complicates things and is closer to integration testing that we want to avoid.
  • Reducers are simple functions at the core of a Redux app, so they have to be thoroughly tested. Testing them is not difficult.
  • Since simple (object) action creators are reaching reducers that are already tested, we don’t have to test these action creators again separately.
  • We want to thoroughly test thunk action creators and make sure that when promises are resolved, we have proper actions (simple objects) trigger.
  • All util/common code (functions) should be fully unit tested.
  • If we want to test whether a button click in one component would trigger something within that same component – that’s ok. But as soon as we want to test interactions between different components that automatically goes more to integration testing and it’s not easy. Why? We can mount the root component in our test, simulate a click event and then assert and run the test, but as soon as we have several connected components in the tree (and we do), it complicates things, and we have to mock the store, pass it in with the Provider, etc.

Tools

There are many different tools and options out there. I did some research, and this is my arsenal of choice:

  • Jest – the default test runner provided by create-react-app by Facebook
  • Jasmine – enables you to write assertions and it’s included with Jest and create-react-app
  • Enzyme – AirBnb’s library for testing React components
  • Redux Mock Store – a library for mocking a Redux store
  • Nock – an HTTP mocking library

These tools are powerful, and in this article, I am not explaining all possible uses; refer to corresponding docs for more details. Fire this one-liner to install everything you need:

npm i -D react-test-renderer @types/jasmine react-addons-test-utils enzyme enzyme-adapter-react-16 redux-mock-store nock

There are also some other tools that I had to install and configure to make everything smooth:

  • @type/jasmine from above, so that Webstorm doesn’t complain about not seeing functions from Jasmine. You don’t have to import them; it’s just for the IDE.
  • I had to install Watchman.
  • In the root of my project, check mocks/popper.js.js. It’s a mock of the popper.js library I had to create, to get the mount function of Enzyme working.

Test Organization

Jest searches for tests folders in your project tree and will run all files/tests from there. So for example – if I am testing reducers, I will create reducers/tests/my-reducer.test.js. I like this filename convention, but you can use anything else.

Testing Dumb Components

Let’s test our first presentational component. Check the src/components/tests/Toolbar.test.js* file and notice the imports, Enzyme configuration/adapter, and the 3 tests we have:

  • Component actually renders – this is our first test, and we are using the shallow function from Enzyme to render only the light/shallow RcToolbar component without any children. Enzyme has jQuery-like syntax, so it’s straightforward to find what you need and assert.
  • Here I am checking that the Add button has the “+” icon from Material UI. I am using the mount function here and not shallow because I need to access a child component to assert.
  • In the last test I am checking if toggle button/filter is turned on by default.

Another good example would be to test if the RcList component renders N li elements when I pass in N countries.

So we are making minimalistic tests for presentational components – do they render and show what they need to show.

Testing Reducers

Now open src/reducers/tests/countries.test.js. The country reducer is a combined reducer for using normalized state, so we are testing both byId and list reducers here.

Testing reducers is really straightforward because they are pure functions. For the given input, we expect always to get the same output. In my example, we are checking if a country-add success will result in a state that contains the newly added country.

Notice that we are using the addCountrySuccess action creator inside the test. That’s why we don’t have to test simple action creators explicitly. Refactor your code, so that you can use these action creators in both code and tests if you already don’t have them set up that way.

Testing Thunk Action Creators

It’s important to test thunk / async action creators to make sure that we have proper simple actions dispatched after a promise has been resolved. Open src/actions/tests/actions.test.js. The first thing you will notice is that we are using the redux-mock-store library to mock a Redux store. We are also adding thunk middleware to it. We are mocking an HTTP request (using the Nock library) since adding a country will result in a remote API call, and we don’t want that in the test. We are then merely dispatching a thunk action and, when done, checking if all expected actions have been dispatched. In this specific case, we expect that adding a country will result in a success event, closed dialog, and a message.

Testing Utils

I don’t have any utility/common code in the project, but these should be fully tested, as they are usually pure functions.

Local Testing

It’s time to run our tests locally with npm test. It’s configured to run only newly added/changed tests from the last commit (more details here). There is also interactive mode allowing you to press “a” to run all tests anyway. This doesn’t work well in Webstorm when npm test is invoked from the UI, so just use the terminal if you want interactive mode.

CI

I am using Travis, and if you check the travis configuration file, you will see that one new line has been added to enable CI testing. NPM testing will be invoked on every commit.

Outro

The testing topic is really complex, and there are many possible options, but I think this approach is relatively simple and tests the core of a React-Redux app – reducers, actions, and presentational components. In combination with a QA team, this can be a good testing strategy. 😉

We'll help you unleash.

Join the 30,000 developers who subscribe to our newsletter.

Scale your
Development team

We help you execute projects by providing trusted developers who can join your team and immediately start delivering high-quality code.

Hire Developers
code, react, redux