Published on

React App Testing Made Simple!

We all know the importance of testing when developing software applications but do you know how to unit test and integration test React applications? If not, keep reading to find out!

How to do basic testing for React applications!

Figure 1: How to do basic testing for React applications!

React Testing Libraries

In order conduct testing on React applications we must use testing libraries to help out with this process. Some popular React testing libraries include:

  • Jest: This is the testing library endorsed by Facebook to test React components and is often recommended as the framework of choice for testing purposes. Here is its homepage
  • Enzyme: Enzyme should not be considered a testing framework but rather a testing utility for React that streamlines the the process testing component outputs though the abstraction of the component rendering process. Here is its homepage
  • React-testing-library: This is more of a testing utility library than an actual testing framework with the main goal of providing simple and all-encompassing set of testing utilities designed to mock end user interactions and workflows. Here is its homepage
  • Mocha: This is a testing framework originally designed for Node.js but it also compatible with React. Note worthy features include great configurability and the ability to use any assertion library. Here is its homepage
  • Chia: This is a BDD and TDD assertion and expectation testing library originally intended for Node.js but also usable with React applications. It's main features include the ability to use keywords such as expect, should, and assert. Here is its homepage
  • Karma: Although Karma is neither a testing library or an assertion framework, it does aid in testing by having the ability to launch HTTP servers and generate test runner HTML files. Here is its homepage
  • Jasmine: Self-proclaimed as a JavaScript testing frameworks for Node.js and modern browsers, Jasmine is a Behavior Testing Framework(more on BDD here) that is compatible with React as well as other JavaScript frameworks. Here is its homepage

Various types of tests

Okay let's get into writing some unit tests for a example React application. I'll be using this React application to write the tests shown in this tutorial but feel free to follow along with any old React application. The main idea to learn from this blog post is how write effective tests on a broad level instead of a detail specific view.

Ok first we navigate into the src/components/signup folder and create a signup.test.js file inside it. Let's start up out by adding the following code to it:

import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import enzymeConfig from '../../enzymeConfig';
import { render, screen, fireEvent } from "@testing-library/react";
import { withMarkup } from "../../testUtils/withMarkup";
import Signup from "./index";
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import {
    ApolloClient,
    InMemoryCache,
    ApolloProvider,
    HttpLink,
} from '@apollo/client';
import { split } from 'apollo-link';
import redux from "../../redux/store";
import { WebSocketLink } from 'apollo-link-ws';
import { GRAPHQL_HTTP_SERVER_URL, GRAPHQL_WS_SERVER_URL } from '../../environment';



global.matchMedia = global.matchMedia || function () {
    return {
      addListener: jest.fn(),
      removeListener: jest.fn(),
    };
};

Ok, firstly we're importing the @testing-library/react testing library along with some important testing dependencies such as jest and enzyme. Then we're declaring the global.matchMedia method in order to prevent potential window.matchMedia errors(see here for a StackOverflow post dedicated to this error).

Now we can begin adding our first unit test in the same signup.test.js file like so:


describe("Signup page should contain important page elements", function() {
    configure({ adapter: new Adapter() })

it('Should render \'Sign Up\' button', function(){
        const element = shallow(<button>Sign up</button>);
        expect(element.find('button').length).toBe(1);
    });
});

The describe()methods houses all test cases for a given test suite. In other words, it represents a test scenario, if we use testing terminology. Next, the it() methods represent each individual test cases in a test suite. As our first test case, we are rendering a <button> element using the enzyme testing utility's shallow() method. Then we use the jest testing library's expect() method to make an assertion: the <button> element should be present in the element container and therefore it should contain a length of one.

Finally, you might be wondering the purpose of the configure() method above the it() method. Well, this line is basically configuring an adapter so that any changes based on the React version of the application can be abstracted away. Ultimately, this makes our tests compatible with future versions of React.

Ok, let's add our second test now:

   it("Should be able to type into \'Email\' field", () => {
        render(
          <Signup />
        );
        const signUpButton = screen.getByText("Sign In");
        fireEvent.click(signUpButton);
        const emailField = screen.getByPlaceholderText("Email");
        fireEvent.click(emailField);
        fireEvent.change(emailField, { target: { value: "bobMarley@gmail.com" }});
        expect(emailField.value).toBe("bobMarley@gmail.com");
    });

This one is a bit more complex than the first one. Let's see what this testcase is doing:

First we use the render() method to render the <Signup /> component. The purpose of this testcase is to validate the ability to type into the email input field in this component. However, the email input field is rendered in a modal that only appears on button click. Therefore as our next step, the fireEvent.click() method is used to click the appropriate button to open the modal. Once the modal has been opened we use the fireEvent.change() method to type into the email input field that is now rendered to the DOM. Finally, we compare the value of the field with the value we just typed into it in order to validate this test case.

On to our third type of testcase!🚀

Before we move forward, let's imagine we have a list of elements, or rather 'tweets' in this example, in a <div/> container for a second like so:

List of tweets🐤

Figure 2: List of tweets🐤

While having in mind the goal of validating the existence of four tweets in this <div/> container we can construct a testcase like so:

import mockTweetsResponse from "../../__mocks__/graphqlResponse";
...

it('Tweets container should contain 4 tweets', function(){
        configure({ adapter: new Adapter() })


        const HomeComponent = render(
            <Home mockTweets={mockTweetsResponse} />
        );

        const result = HomeComponent.container.querySelector(".all-tweets-container");
        expect(result.children.length).toBe(4);
});

First, we configure an adapter so that any React version changes do not adversely affect the application. Then using the render() method, we render the Home component. Notice the passing of the mockTweetsResponse prop into the Home component.

This is done because initially this application uses a Node.js backend with GraphQL integration to recieve the tweets to be displayed in the Home component. However since we are in a testing environment, emulating this workflow is not feasible. Therefore, we create a mock tweets data source, via the mockTweetsResponse object imported from the graphqlResponse.js file, and pass it into the Home component as props.

The Home component having received this mock data, will display it in its <div class="all-tweets-container"> element. Now we use container.querySelector() to target this <div> and using the expect(result.children.length).toBe(4) line we check whether this container contains four tweets or not.

Ok let's move on to the next testcase:

    it('Tweets container should contain a tweet with text context', function(){
        configure({ adapter: new Adapter() })


        const HomeComponent = render(
           <Home mockTweets={mockTweetsResponse} />

        );

        const result = HomeComponent.container.querySelector(".all-tweets-container");
        expect(result.firstChild.textContent).not.toBe("");
    });

This testcase is almost identical to the one we just added but it does contain a subtle difference: it checks for the existence of text in the first tweet of the tweets container. Here is a screenshot to give more context:

Highlighted is the first tweet in the tweets container

Figure 3: Highlighted is the first tweet in the tweets container

Conclusion

Whew! If you made it this far, congrats! You now know the basics of testing for React applications!

Although there are many more types of tests that weren't covered in this blog post, for the sake of brevity let us end it here. Hopefully you now have a better idea of the various types of testing utilities at your disposal as a React developer and how to write some basic React tests.

Thanks for reading this blog post on Basic Testing for React applications!

If you have any questions or concerns please feel free to post a comment in this post and I will get back to you when I find the time.

If you found this article helpful please share it and make sure to follow me on Twitter and GitHub, connect with me on LinkedIn and subscribe to my YouTube channel.