Introduction to User-Centric UI Testing with React Testing Library

Intro to React Testing Library

The @testing-library family of packages helps you test UI components in a user-centric way.

The @testing-library is the primary testing library suggested by Sage Architects.

> The principle behind the library: > The more your tests resemble the way your software is used, the more confidence they can give you.

> Jest is a testing framework. > React Testing Library is just a library used with Jest to provide new testing functionality similar to Enzyme.

Querying

  • Queries are the methods that Testing Library gives you to find elements on the page.
  • screen is an object which has access to every query.
  • There are 3 different types of query get, find and query.
  • Query methods follow a naming pattern of getBy..., findBy..., queryBy... etc. There are also All versions of each to find multiple matches.
import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";

import App from "./App";

describe("App", () => {
  beforeEach(() => {
    render();
  });

  test("has correct UI", () => {
    // Title is on the page
    expect(screen.getByText(/Title/i)).toBeInTheDocument();

    // The create button is not disabled
    const createButton = screen.getByText("Create");
    expect(createButton).not.toBeDisabled();
  });
});

Firing Events

  • Use fireEvent to trigger events on elements found with queries.
  • fireEvent dispatches exactly the events you tell it to and just those – even if those exact events never had been dispatched in a real interaction in a browser.
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";

import App from "./App";

describe("App", () => {
  beforeEach(() => {
    render();
  });

  test("adding a new post", () => {
    // Populate the form inputs
    const textInput = screen.getByLabelText("Title");
    fireEvent.change(textInput, { target: { value: "New Post Title" } });

    // Find and click the create button
    const createButton = screen.getByText("Create");
    fireEvent.click(createButton);

    // Check the post was created and is visible
    expect(screen.getByText(/New Post Title/i)).toBeInTheDocument();
  });
});

User Events

  • userEvent dispatches the events like they would happen if a user interacted with the document directly.
  • Using userEvent over fireEvent is a better way of testing user interaction with components.
  • userEvent is imported from a separate package @testing-library/user-event
import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";

import App from "./App";

describe("App", () => {
  beforeEach(() => {
    render();
  });

  test("adding a new post", async () => {
    // Populate the form inputs
    const textInput = screen.getByLabelText("Title");
    await userEvent.click(textInput);
    await userEvent.type(textInput, "New Great Post Title");

    // Find and click the create button
    const createButton = screen.getByText("Create");
    await userEvent.click(createButton);

    // Check the post was created and is visible
    expect(screen.getByText(/New Great Post Title/i)).toBeInTheDocument();
  });
});

Waiting for Async Methods

  • waitFor is a useful async method which will wait for your expectations to pass. Use when you have async actions.
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";

import App from "./App";

describe("App", () => {
  beforeEach(() => {
    render();
  });

  test("deleting all posts", async () => {
    // Find and click the clear posts button
    const clearButton = screen.getByText("Clear Posts");
    fireEvent.click(clearButton);

    // Check post is gone after deletion
    await waitFor(() => {
      // Check the posts were destroyed and are not visible
      // Use queryByX methods to determine element does not exist
      expect(screen.queryByText(/Post Title/i)).toBeNull();
      expect(screen.queryByText(/Post Body/i)).toBeNull();
    });
  });
});

Debugging

  • Calling screen.debug() will print the HTML for the component. E.G

  <div>
    <div class="app">
    ...
    ...
    ...
    </div>
  </div>

Useful Testing Library Links

  • Queries – Methods to find elements on the page.
  • Query within – How to search for an element within a found element.
  • Firing Events – Use the fireEvent method to fire different events on elements.
    • User Events – Use the userEvent method to fire different events on elements.
  • Async Method – Methods for dealing with asynchronous code.
    • waitForwaitFor is a useful async method which will wait for your expectations to pass.
  • Debugging– Some additional debugging options
    • Can still use the debug() method
    • console.log(screen.debug());

One thought on “Introduction to User-Centric UI Testing with React Testing Library

Leave a comment