Installation and Configuration of React Testing Library with Jest
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!