Testing events in React using enzyme vs react-testing-library

Consider a component with a button, and on click of the button, the state variable counter of the component is updated. This variable is displayed on screen.

For the above functionality, we are going to write the unit test case in enzyme and in react-testing-library. You will understand the difference of writing unit test case in them.

Component Code

Below is the code of the component we are going to test.

import React, { Component } from "react";

export default class App extends Component {
  // counter variable init to 0
  state = {
    counter: 0
  };


  showLog = () => {
    const { counter } = this.state;
    this.setState({
      counter: counter + 1
    });
  };

  render() {
    const { counter } = this.state;
    return (
      <div className="App">
        <div>{counter}</div>
        <button onClick={this.showLog} onMouseEnter={this.showLog}>
          Click me
        </button>
      </div>
    );
  }
}


Unit test in Enzyme

In Enzyme, we get the ability to access the state of the component and the methods (unlike react testing library). So we are going to write three unit test cases for the above component - each focusing on the specific functionality.

  • Unit test case to check that on click of button, the updateCounter function is called. In the below test case, we get the shallow rendered version of app component, and spy on the instance method updateCounter. Then we force refresh the app to register the new spy method. Then we find the button and simulate the click event. In enzyme, the events such as mouse click, mouse over, drag drop events, custom events etc are simulated using simulate function.
it("should call showLog on click of button", () => {
  const app = shallow(<App />);
  const spy = jest.spyOn(app.instance(), "updateCounter");
  app.instance().forceUpdate();
  app.find("button").simulate("click");
  expect(spy).toHaveBeenCalled();
});
  • Unit test case to check that the function updateCounter actually increments the state counter variable. In the below unit test case, we call the function updateCounter and check that the state variable counter is incremented by 1.
it("should update state counter in updateCounter", () => {
  const app = shallow(<App />);
  app.instance().updateCounter();
  expect(app.state("counter")).toEqual(1);
});
  • Unit test case to check the component renders the counter variable. In the below unit test case, we set the state of the component and then get the value of the counter from the screen. We check if the counter variable from screen is same as the state.
it("should show counter from state", () => {
  const app = shallow(<App />);
  app.setState({
    counter: 10
  });
  const counter = app.find(".counter").text();
  expect(counter).toEqual("10");
});

Unit testing in React Testing Library

When it comes to react testing library, as we discussed on the previous article about the differences between react testing library and enzyme, there are no methods available in React Testing Library to test the state or instance methods of component.

Its because react testing library encourages developers to write unit test cases which closely resemble the way user uses the app and to avoid writing brittle tests.

React Testing Library encourages you to test the functionality of the component rather then the implementation.

So for the above component, we are going to test that when the user clicks on the button, the counter variable displayed on the screen is also updated.

import React from "react";
import "@testing-library/jest-dom/extend-expect";
import { render, fireEvent } from "@testing-library/react";
import App from "./app";

describe("App Tests", () => {
  it("should update counter on click of button", () => {
    const { container } = render(<App />);
    const button = container.querySelector("button");
    fireEvent.click(button);
    const counter = container.querySelector(".counter");
    expect(counter).toHaveTextContent("1");
  });
});

We get the rendered component using the code

render(<App />);

This returns the instance of the app, from which we get container that it is rendered in.

const { container } = render(<App />);

We find the button in the component and fire the click event. In react testing library, events are fired using fireEvent.

const button = container.querySelector("button");
fireEvent.click(button);

Then we check that the counter variable displayed on the screen is incremented by 1.

const counter = container.querySelector(".counter");
expect(counter).toHaveTextContent("1");

In order to test the mouseEnter event, replace fireEvent.click with fireEvent.mouseEnter.

List of all the events

To get the list of all events, go to the link event-map.js

Conclusion

The above article is aimed at educating people on how the way we write the unit test case changes from enzyme to react testing library. Read this tutorial to understand the differences between brittle and non-brittle test, and this tutorial to understand the differences between enzyme and react testing library.