DEV Community

Cover image for Unit Testing of React Components with Testing Library
Ashutosh
Ashutosh

Posted on

Unit Testing of React Components with Testing Library

React Testing Library is an alternative to Enzyme. This puts developer in the shoes of end user of react application.

Jest Vs React Testing Library

Jest is the test runner and gives the ability to run the test from the command line. When we write npm test or npm run test it is jest responsibility to collect all the files ending with .test.js, run each test cases and show pass, fail results. React testing Library provides us with the functions to work with the DOM elements like render, fireEvent, waitFor, screen. Jest provides us functions for test suites, test cases, and assertions in the form of describe-block, test-block. A test suite can have multiple test cases and a test case doesn't have to be in a test suite.

import sum from './math.js';

describe('sum', () => {
  test('sums up two values', () => {
    expect(sum(2, 4)).toBe(6);
  });
});
Enter fullscreen mode Exit fullscreen mode

Render a component

Here we discuss the way to render react components.

import React from 'react';

const title = 'Hello React';

function App() {
  return <div>{title}</div>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Selecting Components

React Testing Library offers us tools to search functions to grab elements. These elements are then used for assertions or user interactions.

import React from 'react';
import { render, screen } from '@testing-library/react';

import App from './App';

describe('App', () => {
  test('renders App component', () => {
    render(<App />);

    // implicit assertion
    // because getByText would throw error
    // if element wouldn't be there
    screen.getByText('Search:');

    // explicit assertion
    // recommended
    expect(screen.getByText('Search:')).toBeInTheDocument();
  });
});
Enter fullscreen mode Exit fullscreen mode

Use debug function to check what is rendered on the screen if not sure.

import React from 'react';
import { render, screen } from '@testing-library/react';

import App from './App';

describe('App', () => {
  test('renders App component', () => {
    render(<App />);

    // fails
    expect(screen.getByText('Search')).toBeInTheDocument();

    // succeeds
    expect(screen.getByText('Search:')).toBeInTheDocument();

    // succeeds
    expect(screen.getByText(/Search/)).toBeInTheDocument();
  });
});
Enter fullscreen mode Exit fullscreen mode

Search Types

  1. getByText - As seen in the above example, it is used to select an element by text.
  2. getByLabelText: <label for="search" />
  3. getByPlaceholderText: <input placeholder="Search" />
  4. getByAltText: <img alt="profile" />
  5. getByDisplayValue: <input value="JavaScript" />

We also have two more search variants queryBy and findBy. The main difference between all is getBy returns an element or an error. If it doesn't find it throws error. In order to assert elements which aren't there we can exchange getBy with queryBy. The findBy is used for asynchronous elements. After initial render the component change the screen with response from API. If we want to test the component over the stretch of its first render to its second render due to the resolved promise, we have to write an async test, for this purpose we use findBy

import React from 'react';
import { render, screen } from '@testing-library/react';

import App from './App';

describe('App', () => {
  test('renders App component', async () => {
    render(<App />);

    expect(screen.queryByText(/Signed in as/)).toBeNull();

    expect(await screen.findByText(/Signed in as/)).toBeInTheDocument();
  });
});
Enter fullscreen mode Exit fullscreen mode

We can always use screen.debug(); to verify our results. If you assert for a missing element, use queryBy. Otherwise default to getBy.

To assert multiple elements all search variants can be extended with the All word getAllBy, queryAllBy, findAllBy. Assertive functions happen on the right hand-side of your assertion. In the previous tests, you have used two assertive functions: toBeNull and toBeInTheDocument. Usually all these assertive functions origin from Jest. However, React Testing Library extends this API with its own assertive functions like toBeInTheDocument.

User Interaction: Fire Events

Till now we have rendered components, selected elements to simulate interactions of end user fireEvent function is used.

    fireEvent.change(screen.getByRole('textbox'), {
      target: { value: 'JavaScript' },
    });
Enter fullscreen mode Exit fullscreen mode

The fireEvent takes an element(here textbox) where change needs to be made and the event(here JavaScript).

React Testing Library comes with an extended user event library which builds up on top of the fireEvent API. fireEvent.change() triggers only a change event whereas userEvent.type triggers a change event, but also keyDown, keyPress, and keyUp events. Whenever possible, use userEvent over fireEvent when using React Testing Library.

await userEvent.type(screen.getByRole('textbox'), 'JavaScript');
Enter fullscreen mode Exit fullscreen mode

Change Handlers

Sometimes we donot have state or side-effects, but inputs as props and output as JSX and callbacks. Lets consider the below example.

function Search({ value, onChange, children }) {
  return (
    <div>
      <label htmlFor="search">{children}</label>
      <input
        id="search"
        type="text"
        value={value}
        onChange={onChange}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

We use a utility from Jest to mock the onChange function which is passed to the component.

describe('Search', () => {
  test('calls the onChange callback handler', () => {
    const onChange = jest.fn();

    render(
      <Search value="" onChange={onChange}>
        Search:
      </Search>
    );

    fireEvent.change(screen.getByRole('textbox'), {
      target: { value: 'JavaScript' },
    });

    expect(onChange).toHaveBeenCalledTimes(1);
  });
});
Enter fullscreen mode Exit fullscreen mode

References

  1. React Testing Library Tutorial By ROBIN WIERUCH
  2. How to Start Testing Your React Apps... By Ibrahima Ndaw
  3. Official Docs

Top comments (0)