Setup
React Testing Library
does not require any configuration to be used. However,there are some things you can do when configuring your testing framework toreduce some boilerplate. In these docs we'll demonstrate configuring Jest, butyou should be able to do similar things withany testing framework (React Testing Library does notrequire that you use Jest).
Global Config
Adding options to your global test config can simplify the setup and teardown oftests in individual files.
Custom Render
It's often useful to define a custom render method that includes things likeglobal context providers, data stores, etc. To make this available globally, oneapproach is to define a utility file that re-exports everything fromReact Testing Library
. You can replace React Testing Library with this file inall your imports. Seebelow for a way tomake your test util file accessible without using relative paths.
The example below sets up data providers using thewrapper
option torender
.
- JavaScript
- TypeScript
- import { render, fireEvent } from '@testing-library/react';
+ import { render, fireEvent } from '../test-utils';
importReactfrom'react'
import{render}from'@testing-library/react'
import{ThemeProvider}from'my-ui-lib'
import{TranslationProvider}from'my-i18n-lib'
importdefaultStringsfrom'i18n/en-x-default'
constAllTheProviders=({children})=>{
return(
<ThemeProvidertheme="light">
<TranslationProvidermessages={defaultStrings}>
{children}
</TranslationProvider>
</ThemeProvider>
)
}
constcustomRender=(ui, options)=>
render(ui,{wrapper:AllTheProviders,...options})
// re-export everything
export*from'@testing-library/react'
// override render method
export{customRenderas render}
- import { render, fireEvent } from '@testing-library/react';
+ import { render, fireEvent } from '../test-utils';
importReact,{ReactElement}from'react'
import{render,RenderOptions}from'@testing-library/react'
import{ThemeProvider}from'my-ui-lib'
import{TranslationProvider}from'my-i18n-lib'
importdefaultStringsfrom'i18n/en-x-default'
constAllTheProviders=({children}:{children:React.ReactNode})=>{
return(
<ThemeProvidertheme="light">
<TranslationProvidermessages={defaultStrings}>
{children}
</TranslationProvider>
</ThemeProvider>
)
}
const customRender=(
ui:ReactElement,
options?:Omit<RenderOptions,'wrapper'>,
)=>render(ui,{wrapper:AllTheProviders,...options})
export*from'@testing-library/react'
export{customRenderas render}
Note
Babel versions lower than 7 throw an error when trying to override the namedexport in the example above. See#169and the workaround below.
Workaround for Babel 6
You can use CommonJS modules instead of ES modules, which should work in Node:
const rtl=require('@testing-library/react')
constcustomRender=(ui, options)=>
rtl.render(ui,{
myDefaultOption:'something',
...options,
})
module.exports={
...rtl,
render: customRender,
}
Add custom queries
Note
Generally you should not need to create custom queries forreact-testing-library. Where you do use it, you should consider whether yournew queries encourage you to test in a user-centric way, without testingimplementation details.
You can define your own custom queries as described in theCustom Queries documentation, orvia thebuildQueries
helper. Then you can use them in any render call using thequeries
option. Tomake the custom queries available globally, you can add them to your customrender method as shown below.
In the example below, a new set of query variants are created for gettingelements bydata-cy
, a "test ID" convention mentioned in theCypress.iodocumentation.
- JavaScript
- TypeScript
import{queryHelpers, buildQueries}from'@testing-library/react'
// The queryAllByAttribute is a shortcut for attribute-based matchers
// You can also use document.querySelector or a combination of existing
// testing library utilities to find matching nodes for your query
constqueryAllByDataCy=(...args)=>
queryHelpers.queryAllByAttribute('data-cy',...args)
constgetMultipleError=(c, dataCyValue)=>
`Found multiple elements with the data-cy attribute of:${dataCyValue}`
constgetMissingError=(c, dataCyValue)=>
`Unable to find an element with the data-cy attribute of:${dataCyValue}`
const[
queryByDataCy,
getAllByDataCy,
getByDataCy,
findAllByDataCy,
findByDataCy,
]=buildQueries(queryAllByDataCy, getMultipleError, getMissingError)
export{
queryByDataCy,
queryAllByDataCy,
getByDataCy,
getAllByDataCy,
findAllByDataCy,
findByDataCy,
}
import{
queryHelpers,
buildQueries,
Matcher,
MatcherOptions,
}from'@testing-library/react'
// The queryAllByAttribute is a shortcut for attribute-based matchers
// You can also use document.querySelector or a combination of existing
// testing library utilities to find matching nodes for your query
constqueryAllByDataCy=(
container: HTMLElement,
id: Matcher,
options?: MatcherOptions|undefined,
)=> queryHelpers.queryAllByAttribute('data-cy', container, id, options)
constgetMultipleError=(c, dataCyValue)=>
`Found multiple elements with the data-cy attribute of:${dataCyValue}`
constgetMissingError=(c, dataCyValue)=>
`Unable to find an element with the data-cy attribute of:${dataCyValue}`
const[
queryByDataCy,
getAllByDataCy,
getByDataCy,
findAllByDataCy,
findByDataCy,
]=buildQueries(queryAllByDataCy, getMultipleError, getMissingError)
export{
queryByDataCy,
queryAllByDataCy,
getByDataCy,
getAllByDataCy,
findAllByDataCy,
findByDataCy,
}
You can then override and append the new queries via the render function bypassing aqueries
option.
If you want to add custom queries globally, you can do this by defining yourcustomizedrender
,screen
andwithin
methods:
- JavaScript
- TypeScript
import{render, queries, within}from'@testing-library/react'
import*as customQueriesfrom'./custom-queries'
const allQueries={
...queries,
...customQueries,
}
const customScreen=within(document.body, allQueries)
constcustomWithin=element=>within(element, allQueries)
constcustomRender=(ui, options)=>
render(ui,{queries: allQueries,...options})
// re-export everything
export*from'@testing-library/react'
// override render method
export{customScreenas screen, customWithinas within, customRenderas render}
import{render, queries, within, RenderOptions}from'@testing-library/react'
import*as customQueriesfrom'./custom-queries'
import{ReactElement}from'react'
const allQueries={
...queries,
...customQueries,
}
const customScreen=within(document.body, allQueries)
constcustomWithin=(element: ReactElement)=>within(element, allQueries)
const customRender=(
ui: ReactElement,
options?: Omit<RenderOptions,'queries'>,
)=>render(ui,{queries: allQueries,...options})
export*from'@testing-library/react'
export{customScreenas screen, customWithinas within, customRenderas render}
You can then use your custom queries as you would any other query:
const{getByDataCy}=render(<Component/>)
expect(getByDataCy('my-component')).toHaveTextContent('Hello')
Configuring Jest with Test Utils
To make your custom test file accessible in your Jest test files without usingrelative imports (../../test-utils
), add the folder containing the file to theJestmoduleDirectories
option.
This will make all the.js
files in the test-utils directory importablewithout../
.
- import { render, fireEvent } from '../test-utils';
+ import { render, fireEvent } from 'test-utils';
module.exports = {
moduleDirectories: [
'node_modules',
+ // add the directory with the test-utils.js file, for example:
+ 'utils', // a utility folder
+ __dirname, // the root directory
],
// ... other options ...
}
If you're using TypeScript, merge this into yourtsconfig.json
. If you'reusing Create React App without TypeScript, save this tojsconfig.json
instead.
{
"compilerOptions":{
"baseUrl":"src",
"paths":{
"test-utils":["./utils/test-utils"]
}
}
}
Jest 28
If you're using Jest 28 or later, jest-environment-jsdom package now must beinstalled separately.
- npm
- Yarn
npminstall --save-dev jest-environment-jsdom
yarnadd --dev jest-environment-jsdom
jsdom
is also no longer the default environment. You can enablejsdom
globally by editingjest.config.js
:
module.exports = {
+ testEnvironment: 'jsdom',
// ... other options ...
}
Or if you only needjsdom
in some of your tests, you can enable it as and whenneeded usingdocblocks:
/**
* @jest-environment jsdom
*/
Jest 27
If you're using a recent version of Jest (27),jsdom
is no longer the defaultenvironment. You can enablejsdom
globally by editingjest.config.js
:
module.exports = {
+ testEnvironment: 'jest-environment-jsdom',
// ... other options ...
}
Or if you only needjsdom
in some of your tests, you can enable it as and whenneeded usingdocblocks:
/**
* @jest-environment jsdom
*/
Jest 24 (or lower) and defaults
If you're using the Jest testing framework version 24 or lower with the defaultconfiguration, it's recommended to usejest-environment-jsdom-fifteen
packageas Jest uses a version of the jsdom environment that misses some features andfixes, required by React Testing Library.
First, installjest-environment-jsdom-fifteen
.
- npm
- Yarn
npminstall --save-dev jest-environment-jsdom-fifteen
yarnadd --dev jest-environment-jsdom-fifteen
Then specifyjest-environment-jsdom-fifteen
as thetestEnvironment
:
module.exports = {
+ testEnvironment: 'jest-environment-jsdom-fifteen',
// ... other options ...
}
Using without Jest
If you're running your tests in the browser bundled with webpack (or similar)thenReact Testing Library
should work out of the box for you. However, mostpeople using React Testing Library are using it with the Jest testing frameworkwith thetestEnvironment
set tojest-environment-jsdom
(which is the defaultconfiguration with Jest 26 and earlier).
jsdom
is a pure JavaScript implementation of the DOM and browser APIs thatruns in Node. If you're not using Jest and you would like to run your tests inNode, then you must install jsdom yourself. There's also a package calledglobal-jsdom
which can be used to setup the global environment to simulate thebrowser APIs.
First, installjsdom
andglobal-jsdom
.
- npm
- Yarn
npminstall --save-dev jsdom global-jsdom
yarnadd --dev jsdom global-jsdom
With mocha, the test command would look something like this:
mocha --require global-jsdom/register
Skipping Auto Cleanup
Cleanup
is called after each test automatically by defaultif the testing framework you're using supports theafterEach
global (likemocha, Jest, and Jasmine). However, you may choose to skip the auto cleanup bysetting theRTL_SKIP_AUTO_CLEANUP
env variable to 'true'. You can do this withcross-env
like so:
cross-env RTL_SKIP_AUTO_CLEANUP=true jest
To make this even easier, you can also simply import@testing-library/react/dont-cleanup-after-each
which will do the same thing.Just make sure you do this before importing@testing-library/react
. You coulddo this with Jest'ssetupFiles
configuration:
{
// ... other jest config
setupFiles:['@testing-library/react/dont-cleanup-after-each']
}
Or with mocha's-r
flag:
mocha --require @testing-library/react/dont-cleanup-after-each
Alternatively, you could import@testing-library/react/pure
in all your teststhat you don't want thecleanup
to run and theafterEach
won't be setupautomatically.
Auto Cleanup in Mocha's watch mode
When using Mocha in watch mode, the globally registered cleanup is run only thefirst time after each test. Therefore, subsequent runs will most likely failwith aTestingLibraryElementError: Found multiple elements error.
To enable automatic cleanup in Mocha's watch mode, add a cleanuproot hook. Create amocha-watch-cleanup-after-each.js
file with the following contents:
const{cleanup}=require('@testing-library/react')
exports.mochaHooks={
afterEach(){
cleanup()
},
}
And register it using mocha's-r
flag:
mocha --require ./mocha-watch-cleanup-after-each.js
Auto Cleanup in Vitest
If you're using Vitest and want automatic cleanup to work, you canenable globals through its configurationfile:
import{defineConfig}from'vitest/config'
exportdefaultdefineConfig({
test:{
globals:true,
},
})
If you don't want to enable globals, you can importcleanup
and call itmanually in a top-levelafterEach
hook:
import{defineConfig}from'vitest/config'
exportdefaultdefineConfig({
test:{
setupFiles:['vitest-cleanup-after-each.ts'],
},
})
import{cleanup}from'@testing-library/react'
import{afterEach}from'vitest'
afterEach(()=>{
cleanup()
})