Introduction to testing React applications

There are different types how you can test React Applications. Inpsyder Emili takes a look at a few types and explains them.

Ever thought about testing React applications? Here’s Inpsyder Emili’s contribution to our Inpsyde Advent Calendar with different types of testing. He does not only explain but also gives helpful examples. Enjoy reading and imitating.


In this article I’m going to introduce you to the most common types of testing used in React applications. I’ll cover Unit, React components and Redux testing.

I am going to use Jest testing library along other libraries like Enzyme for testing React components.

To accompany the concepts explained in this article I created a little React application that you can find here.

Unit Test

Let’s start with Unit testing which tests units of code in isolation. In the following example we have a sum method that returns the sum of two numbers. So, the most basic unit test for sum method could be that we expect the correct result of two given numbers:

function sum(a, b) {
return a + b;
}

test('sum', function () {
expect(sum(1, 2)).toBe(3);
});

As you can see in the test above, we expect that passing 1 and 2 to sum, the result will be 3. Otherwise an error will be displayed.

At this point you can start thinking about more cases to cover. What happens if I pass a string instead of a number? And so on … . The idea here is to try to cover as many cases as possible in order to make this method as robust and less error prone as possible.

An important concept of unit testing is isolation. Ideally, you should test the functionality without any external dependency.  The reason behind it is that if the test fails, it will be difficult to know where the error comes from. Here is where the concept of test doubles comes into place. Like in the movies, you create these doubles to emulate the external dependencies with a known state so there won’t be side effects from external dependencies.

React Component Testing

In order to test React components, we are going to use Enzyme library. It allows us to create React component instances to easily test its output and behavior.

In the first test example we are going to test the App component. We assert that it renders both Sum and Multiply components:
See code here

Because we are checking the existence of children components in the App, we use Full Rendering API (Mount). It renders not only the main component but also the full DOM including its children components. If you only need to do assertions of a single component, you can use Shallow Rendering API that only renders the current component.

The next thing we can test is component behavior. To do so we are going to test whether the Sum component works as expected or not:

https://github.com/Dinamiko/react-test-app/blob/master/src/components/Sum.js

test('sum two numbers', () => {
    component.find('#a').simulate('change', {
        target: {
            value: 1
        }
    });
    component.find('#b').simulate('change', {
        target: {
            value: 2
        }
    });

    component.find('button').simulate('click');

    expect(component.find('#result').prop('value')).toEqual(3);
});

As you can see in the example above, we use Simulate to simulate events in the component. We use change event to fill values in text fields, and click event to trigger a click from the button. After that we do an assertion about the value in the result field.

Component testing in not only limited to testing individual components. You can also easily create integration tests involving multiple components in the same test using the same techniques described above.

Redux Testing

Before digging into Redux Testing we are going to quickly see how Redux works and which problems it solves.

Redux is a state container library that allows you to manage the state of the application from one single place. This concept is known as Store. For example, Redux is useful  when your application grows in complexity and you need to pass data through components. With Redux you can grab and pass data at any time from any component. It doesn’t matter how deeply nested you are in the component hierarchy.

The building blocks of Redux are Action Creators and Reducers. Here is a diagram showing the whole process:

Testing React Applications overview

In the example above we are calling doMultiply Action Creator that dispatches a MULTIPLY action type to the Reducer. Then the Reducer updates the state and returns it back to the component. In the component we use Connect from React Redux library which allows us to map state to props that can be used in the component as props. In the second parameter of connect (mapDispatchToProps) we pass an object to wire the Action Creators like so:

export default connect(mapStateToProps, actions)(Multiply);

Testing Reducers

In the following example we test that sending a MULTIPLY action type to the Reducer, it updates the state correctly:

test('handles MULTIPLY action type', () => {
    const action = {
        type: MULTIPLY,
        payload: 123
    };

    const newState = math({}, action);

    expect(newState).toEqual({result: 123});
});

First we create an action object with a type MULTIPLY and a payload value. Then we create an instance of the Reducer passing the action as a second parameter. Finally we assert that the new state contains the expected result.

Testing Action Creators

Regarding Action Creators we are testing that it contains the correct type and in case that it includes a payload, check that the value sent in the payload is the expected one:

test('has correct type', () => {
    const action = doMultiply();
    expect(action.type).toEqual(MULTIPLY);
});

test('has correct payload', () => {
    const action = doMultiply(2, 3);
    expect(action.payload).toEqual(6);
});

Testing Asynchronous State

For the sake of simplicity we are not going to cover Asynchronous State in this article, since it deserves an article of its own. Basically, in order to make Redux being able to manage Asynchronous State, you need to put a Middleware between the Action Creator and the Reducer. This is because we need to wait an amount of time until we receive a response. Here is where the use of Middlewares like redux-promise or redux-thunk comes into place.

Do you have any questions? Don’t hesitate to ask!