Testing a React & Redux app — a comprehensive guide (part 1)

David Alecrim
Jun 26 · 12 min read

These series of posts will, hopefully, give you a good overview and examples on how you can do unit and integration tests for a react & redux app, using Jest and Enzyme.

The series will be split into 4 parts:

Part 1 — Introduction to tests (theory) & first real world practical example:

Part 2 — Second real world practical example:

Part 3 — Third real world practical example:

Part 4 — Fourth real world practical example:

TL;DR

If you already know the theory behind the kinds of tests that exist and the difference between them, then skip straight to the “Show me the good stuff” section for some practical examples of using Jest and Enzyme to perform unit and integration tests in React & Redux.


First of all, I hope we all agree that to develop software nowadays, testing is a fundamental part of the process. Now, I’m not saying it’s the most important part, or that it should be the first thing you do (e.g. TDD or Test Driven Development), because each software is different and has different needs, but it should definitely be well implemented and thought of in your software development process.

So, without further ado, what is this about unit testing and integration testing? Are there more types of testing? Which types of testing should I do for my application? How do I test my app? These are all valid questions, and questions that are asked several times by many software developers. I’ll try to do a brief introduction for this and clarify some concepts.

So to test our app, we can do several types of tests, like we can see in the well known testing pyramid:

Testing pyramid

Its essential point is that you should have many more low-level UnitTests than high level BroadStackTests running through a GUI. — Martin Fowler

This pyramid illustrates the use of 3 different kinds of well known types of tests, namely, testing, testing and testing.

Regarding unit testing we can define it as such:

Basically it’s the most low level tests we’ll have in our testing suite and will test if very small parts of our code are working as expected. It’s expected that unit tests will be your most powerful “testing force”, covering the most part of your code and being invaluable for regression testing (testing everything again after performing a change to the software). For example, if we have a function “sum” that sums two numbers, some possible unit tests for that would be something like:


Now, let us pass to integration testing i.e. the service layer of the pyramid.

At this point you might be wondering “Well, ok, it tests an integration between two small components for example. But how does that apply to react?”, and I answer: Each component defined in React is an individual piece of the system and by testing communication and interaction between two different components we are technically performing integration testing.

As the second layer, it’s expected for there to be a good number of integration tests, but not as many as unit tests, at least, in theory.

Let’s analyse the following app case and then create an integration test for it:

Given something like these two components, how could we perform integration testing on them?

So, this is a possible integration test on the previous study case we presented. It’s a super simple example, so nothing too fancy until now.


Now for the top layer of the pyramid, the end-to-end tests.

These tests are in smaller number, take much longer to execute and have a different purpose than the previous tests until now. These kind of tests simulate how the end user interacts with the system (hence end-to-end).

It usually involves simulating clicks, key presses, waiting for elements to appear on screen and validating if everything is according to the coded flow of the application. By testing the flow of the app vs the code itself, end-to-end tests validate the results of the system as a whole.

As end-to-end tests are out of the scope of this article, I won’t give any study cases or test examples, but if you really want to hear more about them please drop me a message in the comments below and I can write another article on it 😉.

Let’s keep in mind that these types of tests are not the only types of tests that exist! There are more test classifications, but I’m only going to cover these, for simplicity and pragmatism, since these are VERY important.

The testing side of a project is a world, there is so much material, so many frameworks and tools that it’s impossible to cover everything in a single article (or even in a series of articles).

Now that the boring part is out of the way, let’s dive in to some practical examples.


Show me the good stuff

Each project has its own different needs, which in turn have their own test quirks and particularities.

We’ll not be working on a full fledged project as a case study, instead we will be working on different specific real world needs that may arise during your own projects.

Test setup with jest and enzyme

Let’s cover the setup part of this with two different cases. One that uses the create-react-app cli tool and other that does not use it and was created in a different way.

With create-react-app

First, let’s install the cli tool in our system:

npm i -g create-react-app

Then we can create a new test project with:

npx create-react-app test_project (requires npm@5.2.0)

cd test_project

At the time of writing, create-react-app ships with React@16.x.

So, in a create-react-app project we already have jest installed, which is great! Now we just need to install enzyme in the project (use yarn or npm as you wish). There’s a twist here for enzyme to work out of the box with our project, we also need to install the correct enzyme adapter and configure it:

npm i --save-dev enzyme enzyme-adapter-react-16

The part may vary depending if you’re using React@15.x or other. You just need to change the last part to for example.

So, in the projects created now by create-react-app we can configure our test setup in a file that jest will search for automatically. It will search for a setupTests.js in the src directory of the project.

Basic setup for enzyme to work in our tests (setupTests.js file):

So now we can do some tests at will with both jest and enzyme! :)

Without create-react-app

Let’s install what we need to create our tests in a React project not created with the previous tool.

yarn add --dev jest babel-jest enzyme enzyme-adapter-react-16

In our project’s package.json we can configure jest options like the following:

On line 3, this option is to ignore test cases found in the directories defined (vendor/* and node_modules/* in this case).

On line 7 this is the setup file for our tests or entry point, and can be named whatever you want. It’s where our test setup is defined (adapter for enzyme and other global variables needed for testing).

On line 10 we can see the reason why we installed babel-jest. It’s for the case when we use components that imported css, less or sass files, and this way we make jest accept these kind of files.

This makes a basic setup for our tests, in order for jest to have what it needs to work. There are many more options, and we will cover some cases where most probably we will need to further customise it.

So now we can do some tests if we used the cli tool to create our project or not!

A quick note

So the question is if you should use them right? In my opinion you should definitely use them IF the project already has some solid features done, which you don’t think will change regularly. Snapshot testing is very useful in more mature projects, but if you are starting a project from scratch and you want to do meaningful tests, then you should avoid them at the start. This is due to the fact that when you are starting a project from scratch things will change A LOT and if you are always rewriting the snapshot test results, you are basically doing nothing to assure code quality. So keep this is mind when thinking of using snapshot tests.


Study case 1 — Filterable table

Let’s create a use case where we have a table of todos, and these todos can be filtered. The todos have two fields that are relevant to us: the title and the status (completed or not). We will be able to filter the results of our list by the status attribute. Besides this, we will also be able to choose the number of results per page we want in our table and also navigate between table pages by selecting the page we want.

Since this example is a bit bigger than previous ones, I ask you that you clone this repo or view it on GitHub: https://github.com/comoser/test-article-filterable-table, so you can actually run it locally and see exactly what it does.

If you don’t want to run it locally or you just need to quickly check the project, you can visit it live in here: https://test-article-filterable-table.herokuapp.com.

If you check out the source code, you can find all the tests done for the app under the __tests__ directory.

Unit Tests

In the example app given we can verify some unit tests for the more atomic components, and we can analyse the unit tests done for the number_of_results_picker.js component.

The NumberOfResultsPerPage component is responsible for determining how many results we have in our table per page. It has three possible values: , and . It is a select like component. Here are the unit tests for this component:

So, first of all on line 8 we are describing the suite of tests and naming it, in order for our tests to be logically placed inside a “container”. To create one of these containers you just need to call the describe function and use it as it is in the example. These containers allow us to not only logically group tests, but also to have access to optional methods, like beforeEach, afterEach, beforeAll, afterAll that will only affect the tests in the container’s scope.

On line 11 we are describing the beforeEach function which will run before each test will run, so it’s a good place to put some variables you wish to reset at every test run in the suite. In this function we are calling our component NumberOfResultsPicker and passing our desired props.

On line 20 we specify the first real test, with the test function. It also takes a name parameter, which allows us to identify the specific test when running the whole suite. On our first two tests, we do fairly simple stuff, just making sure the correct html elements are there, that the classes are ok, and that the options of the select are the correct ones when the select is open.

On line 38 we can find our third test of this suite, and this one has a bit more interest than the previous ones. We define our callback that is received by prop, which is called onResultsPerPageChange and we are simulating what it would do if called like it is defined in the actual react component. This allows us to test the component correctly without changing logic. After changing the number of results per page, we are checking if the correct value is then set in the select component.

In the React context I consider these kinds of tests to be unitary like we previously saw in the theory part, since we are testing the functionality and correctness of our most granular and atomic components on the app.

Integration Tests

In our app we have a top level component called FilterableList which calls our Filters component and our List component. In turn, our List component calls both the Pagination and the NumberOfResultsPicker components. So to present an example of integration tests, what better example than the FilterableList component which integrates 4 different components?

On line 8 we are declaring the dummy content of 3 different pages, so we can simulate the pages being changed. In total there are 30 results, and the default results per page are 10.

On our second test starting on line 187 we are testing if by changing the filters on the table, if the results reflect that change.

We start by confirming that the list items in the table by default have both the completed and not completed status present (since the filters are on by default). Then we simulate the change of the filter to only see the todos that are completed. In this simulation we need to perform the change that our API would return (the filtered result), and we do this by doing PAGES[1].filter(todo => todo.completed === true) . After this change we confirm that the list items indeed only have the completed status present in them, and thus verify that the integration between the list and the filters is working correctly.

We follow the same kind of logic for the remaining tests of the component, which will verify all the integrations between the involving components.

Conclusion

This is the first part of a 4 part series of articles regarding testing in a React & Redux app, so stay tuned for the next parts 😉.

Hopefully this first part will help you understand both the theory and the practical part of doing tests in an app, and it can really help you even in an enterprise grade application.

There are several more tests that could be done for this example app, the point here is not to do exhaustive testing, but to do some of the most usual and common cases in real world examples so that you can have a base for testing in your own app.

I would love your feedback 🙂
If you find this article interesting, please share it because you know — Sharing is caring!

Also, if you enjoy working at a large scale in projects with global impact and if you enjoy a challenge, please reach out to us at xgeeks! We’re always looking for talented people to join our team 🙌

xgeeks Engineering

Exceptional | Expertise | Execution

Thanks to Luis Oliveira.

David Alecrim

Written by

Software Engineer always looking for interesting tasks. Part-time traveller ✈️

xgeeks Engineering

Exceptional | Expertise | Execution