Installation and Configuration of React Testing Library with Jest

Kevin Firko
Published:

React Testing Library is a newer library that simplifies the testing of React components.

It provides utility functions built on top of react-dom and react-dom/test-utils that enable developers to write intuitive tests that relate to how actual users interact with your app.

This post covers the installation and configuration of React Testing Library in a React project that was “created from scratch”, i.e. not based on Create React App (CRA).

This post covers common considerations for a modern react project seen in industry:

  • Support for ES6+ language features via babel
  • Support for testing custom hooks
  • CSS Modules / SCSS Modules for component styling
  • Support for components that import multimedia files such as images, fonts, and audio
  • Support for the react-spring animation library.
  • Working with eslint

Installing React Testing Library

Change your working directory to the root of your project.

Use your favorite package manager to install react-testing-library and jest-dom as dev dependencies. The following examples use yarn:

yarn add --dev @testing-library/react
yarn add --dev @testing-library/jest-dom

I like to install jest on its own:

yarn add --dev jest

I recommend including the jest plugin for eslint:

yarn add --dev eslint-plugin-jest

Add the babel-jest package to support the latest ES6+ language features:

yarn add --dev babel-jest

If you’d like to be able to test custom hooks, install @testing-library/react-hooks and react-test-renderer. The version of react-test-renderer should match the version of React, so edit the version number in the example below to reflect what you see in package.json:

yarn add --dev @testing-library/react-hooks
yarn add --dev react-test-renderer@^16.12.0

Note the minimum supported version of React for @testing-library/react-hooks and react-test-renderer is 16.9.0.

Check out the hooks testing documentation at: https://react-hooks-testing-library.com/usage/basic-hooks.

Finally, install the identity-obj-proxy package to support CSS/SCSS Modules. When configured, all classNames defined in component will be returned as-is when run in tests vs. replaced with a hashed version as they would in a production build. This helps facilitate snapshot testing because you can rely on classNames being static. If you are not using CSS/SCSS Modules, you can skip this step:

yarn add --dev identity-obj-proxy

Configuring ESLint

Eslint will complain about your tests in its default configuration. This is because, among other things, your test files will use functions such as test() and expect() without explicitly including them.

Open your eslint configuration file to make some changes to support jest. Your eslint configuration could be housed in a .eslintrc.json file or a functional equivalent such as any .eslintrc.* file or via an eslintConfig key in package.json.

To configure eslint to work with jest, ensure that there is a "jest": true entry in the env section of your configuration:

"env": {
    "commonjs": true,
    "jest": true,
    "es6": true
  },

Add the "jest" plugin to the plugins array:

  "plugins": [
    "babel",
    "react",
    "jest"
  ],

If you’d like to use the recommended linter rules for jest, add plugin:jest/recommended to the extends array. In the example below, I also add the recommended style rules provided by plugin:jest/style:

  "extends": [
    "eslint:recommended",
    "plugin:jest/recommended",
    "plugin:jest/style"
  ],

Supporting Static File Imports

Jest tests will cough if your React components import images, fonts, audio, and other static files. This is because the components are not being run through webpack (or whatever asset bundler you may be using). Imported files such as JPG images will be interpreted as JavaScript code, and that simply won’t work. We can solve this issue with a mock.

Create a test/ folder in the root of your project, and add a __mocks__ subfolder.

Note that this particular file/folder structure is entirely optional; you may wish to use something different in your project.

Create a file fileMock.js and paste the following JavaScript code inside:

module.exports = 'test-file-stub'

In the next section of this post, we will configure jest to use fileMock.js.

Configuring Jest

Jest can be configured by adding a jest.config.js file to the root of your project folder, or via a top-level jest key in package.json.

An example of a basic jest.config.js follows. This particular configuration supports babel via babel-jest, tells jest to look for tests under app/ and test/ folders, and tells it to ignore the node_modules/ and public/ paths.

Consider the patterns defined in the testMatch array. You should revise these to suit your needs. For example, many projects place code in a src/ folder vs. an app/ folder. The following example will match any files ending in .test.js or .test.jsx in the app/ folder, and any files ending in .test.js in the test/ folder.

module.exports = {
  roots: ['<rootDir>'],
  transform: {
    '\\.(js|jsx)?$': 'babel-jest',
  },
  testMatch: [
    '<rootDir>/app/**/*.test.{js, jsx}',
    '<rootDir>/test/**/*.test.js'
  ],
  moduleFileExtensions: ['js', 'jsx', 'json', 'node'],
  testPathIgnorePatterns: ['/node_modules/', '/public/'],
  setupFilesAfterEnv: [
    '@testing-library/jest-dom/extend-expect',
  ],
  moduleNameMapper: { },
}

An example of an expanded configuration that supports CSS Modules / SCSS Modules, imports of images/svg’s/fonts/audio files, and the react-spring library follows.

Note the moduleNameMapper object and how we’ve added a mapping for stylesheet files, multimedia imports, and react-spring. Note how we reference the fileMock.js file we created earlier.

If you do not use react-spring in your project, you can omit the 2x entries for it.

module.exports = {
  roots: ['<rootDir>'],
  transform: {
    '\\.(js|jsx)?$': 'babel-jest',
  },
  testMatch: [
    '<rootDir>/app/**/*.test.{js, jsx}',
    '<rootDir>/test/**/*.test.js'
  ],
  moduleFileExtensions: ['js', 'jsx', 'json', 'node'],
  testPathIgnorePatterns: ['/node_modules/', '/public/'],
  setupFilesAfterEnv: [
    '@testing-library/jest-dom/extend-expect',
  ],
  moduleNameMapper: {
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/__mocks__/fileMock.js',
    'react-spring/renderprops': '<rootDir>/node_modules/react-spring/renderprops.cjs', // define this entry before 'react-spring'
    'react-spring': '<rootDir>/node_modules/react-spring/web.cjs',
  },
}

Full documentation for the configuration file can be found on the Jest website: https://jestjs.io/docs/en/configuration.

Running Tests

In the scripts section of your package.json file, define scripts for test, test:watch, and test:coverage that invoke jest:

"scripts": {
    ...
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage --colors",
    ...
}

You can now run these scripts via your package manager, e.g. yarn test or yarn test:watch.

The watch feature will monitor your project files for changes and automatically run your tests when they do. The coverage tool attempts to calculate your test coverage.

Write Some Tests

There are plenty of resources on how to write tests with react-testing-library and with jest.

Get started with the react-testing-library docs at:

https://testing-library.com/docs/react-testing-library/example-intro

Kent C. Dodds, one of the authors of react-testing-library, has a great blog where he covers topics related to testing and using the library, and he provides plenty of examples:

https://kentcdodds.com/blog/?q=testing

An example of a very basic test for a hypothetical button component called ActionButton follows:

import React from 'react'

import { render } from '@testing-library/react'
import ActionButton from './ActionButton.jsx'

const testLabel = 'TEST_LABEL'

test('confirm ActionButton renders with label', () => {
  const { getByText } = render(<ActionButton buttonStyle="back" label={testLabel} />)
  expect(getByText(testLabel)).toBeInTheDocument()
})

I think one of the coolest features of react-testing-library is fireEvent() function, enabling you to test how your components behave in response to clicks, keypresses, and other events. This provides powerful capabilities for integration testing.

import { render, fireEvent } from '@testing-library/react'
...
fireEvent.touchStart(getByText(/Accept/i))

Check out the ‘cheatsheet’ included in the react-testing-library docs:

https://testing-library.com/docs/react-testing-library/cheatsheet

Good luck with your tests!