There are many libraries available for React.JS testing. You would have probably heard about Jest, Enzyme, React Test Utils, React Testing Library, Dom Testing Library and many more. There are other libraries that help you assert in the unit testing library like chai, expect, sinon.

In this tutorial, we are going to look into testing react.js applications using a library called React Testing Library.

Introduction to react-testing-library

React Testing Library logo

React Testing Library is a lightweight testing library aimed at providing good test practices. What are these best practices? One of the rules for testing is that the unit tests should not be brittle. The unit tests should test the functionality of the components/function and not its implementation. react-testing-library is meant to be the replacement for enzyme, another popular testing library from Airbnb.

How react-testing-library helps write better tests?

Dom Testing Library (which powers react-testing-library) logo

react-testing-library is a wrapper on top of dom-testing-library. The primary goal of  dom-testing-library is to test just as your user uses the application. Your users do not care how you are implementing the functionality, they care if it works as intended. By writing tests that closely resemble the way users use your system, you cut down on writing un-necessary tests and catch bugs faster.

react-testing-library also does not provide methods to test on the instance of the reactjs component. You only get the methods to test the dom, which resembles the way the user uses your application. Because you do not have methods to get an instance of the component, you do not write tests that test the implementation of the component.

Getting started with react-testing-library

Install the library in your reactjs project using the command

npm install react-testing-library --save-dev

So you package.json should be updated with devDependencies with following "react-testing-library". At the time of writing this tutorial, the latest version of react-testing-library was 6.1.2, it might be different when you install the library.

"devDependencies": {
  ...
  "react-testing-library": "^6.1.2"
  ...
},

Writing the test

We are going to take an example of a simple React component that has a button that increments the number and displays it on screen. (If you have read other Techdomain tutorials, this example is our choice, but it is really simple to understand)

This is the example of App.js component

import React, { Component } from 'react';

export default class App extends Component {
  state = {
    number: 0
  }

  incrementNumber = () => {
    const { number } = this.state;
    this.setState({
      number: number + 1
    })
  }

  render() {
    const { number } = this.state;
    return (
      <div>
        <div data-testid="increment-number">
          {number}
        </div>
        <button data-testid="increment-button" onClick={this.incrementNumber}>
          Increment Number
</button>
      </div>
    )
  }
}

We are going to use two APIs from react-testing-library

  • render - This method renders a React element into the DOM and returns utility functions for testing the component.
  • fireEvent - The `fireEvent` method allows you to fire events to simulate user actions.
import { render, fireEvent } from 'react-testing-library';

We will test the functionality of the component - when the user clicks on the button, the updated number should be displayed on the screen.

This is the test for the above functionality.

import React from 'react';
import { render, fireEvent } from 'react-testing-library';
import App from './App';


describe('App Tests', () => {
  it('should increment number', async () => {

    const wrapper = render(<App />);
    let index = await wrapper.findByTestId('increment-number');
    expect(index.textContent).toEqual("0");

    fireEvent.click(wrapper.getByTestId('increment-button'))
    index = await wrapper.findByTestId('increment-number');
    expect(index.textContent).toEqual("1");
  })
})

We are using an async function here because of some of the methods of react-testing-library return promise (like findByTestId).

We get the instance of app using render function

const wrapper = render(<App />);

Then we search for the DOM using the data-testid attribute for the number to check if it shows the initial state as 0.

let index = await wrapper.findByTestId('increment-number');
expect(index.textContent).toEqual("0");

Using fireEvent, we fire a click event from the increment button.

fireEvent.click(wrapper.getByTestId('increment-button'))

Then we test if the number on screen is incremented by 1

index = await wrapper.findByTestId('increment-number');
expect(index.textContent).toEqual("1");

Conclusion

It's very easy to get started with react-testing-library and write maintainable tests. To sum it up, using react-testing-library, you are going to save time writing an un-necessary test and write maintainable tests that do not break when someone changes the functionality of the component.