react-testing-library
Simple and complete React DOM testing utilities that encourage good testing practices.



The problem
You want to write maintainable tests for your React components. However, the
de facto standard for testing (enzyme) is
bloated with complexity and features, most of which encourage poor testing
practices (mostly relating to testing implementation details).
This solution
The react-testing-library
is a very light-weight solution for testing React
components. It provides light utility functions on top of react-dom
and
react-dom/test-utils
, in a way that encourages better testing practices.
Table of Contents
Installation
This module is distributed via npm which is bundled with node and
should be installed as one of your project's devDependencies
:
npm install --save-dev react-testing-library
This library has a peerDependencies
listing for react-dom
.
Usage
import React from 'react'
import {render, Simulate, flushPromises} from 'react-testing-library'
import axiosMock from 'axios'
import Fetch from '../fetch'
test('Fetch makes an API call and displays the greeting when load-greeting is clicked', async () => {
axiosMock.get.mockImplementationOnce(() =>
Promise.resolve({
data: {greeting: 'hello there'},
}),
)
const url = '/greeting'
const {queryByTestId, container} = render(<Fetch url={url} />)
Simulate.click(queryByTestId('load-greeting'))
await flushPromises()
expect(axiosMock.get).toHaveBeenCalledTimes(1)
expect(axiosMock.get).toHaveBeenCalledWith(url)
expect(queryByTestId('greeting-text').textContent).toBe('hello there')
expect(container.firstChild).toMatchSnapshot()
})
Simulate
This is simply a re-export from the Simulate
utility from
react-dom/test-utils
. See the docs.
flushPromises
This is a simple utility that's useful for when your component is doing some
async work that you've mocked out, but you still need to wait until the next
tick of the event loop before you can continue your assertions. It simply
returns a promise that resolves in a setImmediate
. Especially useful when
you make your test function an async
function and use
await flushPromises()
.
See an example in the section about render
below.
render
In the example above, the render
method returns an object that has a few
properties:
container
The containing DOM node of your rendered React Element (rendered using
ReactDOM.render
). It's a div
. This is a regular DOM node, so you can call
container.querySelector
etc. to inspect the children. Tip: To get the root
element of your rendered element, use container.firstChild
.
queryByTestId
A shortcut to container.querySelector(`[data-test="${yourId}"]`)
. Read
more about data-test ids below.
const usernameInputElement = queryByTestId('username-input')
More on data-test
ids
The queryByTestId
utility is referring to the practice of using data-test
attributes to identify individual elements in your rendered component. This is
one of the practices this library is intended to encourage.
Learn more about this practice in the blog post:
"Making your UI tests resilient to change"
FAQ
How do I update the props of a render component?
It'd probably be better if you test the component that's doing the prop updating
to ensure that the props are being updated correctly (see
the Guiding Principles section). That said, if you'd
prefer to update the props of a rendered component in your test, the easiest
way to do that is:
const {container, queryByTestId} = render(<NumberDisplay number={1} />)
expect(queryByTestId('number-display').textContent).toBe('1')
render(<NumberDisplay number={2} />, {container})
expect(queryByTestId('number-display').textContent).toBe('2')
Open the tests
for a full example of this.
If I can't use shallow rendering, how do I mock out components in tests?
In general, you should avoid mocking out components (see
the Guiding Principles section). However if you need to,
then it's pretty trivial using
Jest's mocking feature.
One case that I've found mocking to be especially useful is for animation
libraries. I don't want my tests to wait for animations to end.
jest.mock('react-transition-group', () => {
const FakeTransition = jest.fn(({children}) => children)
const FakeCSSTransition = jest.fn(
props =>
props.in ? <FakeTransition>{props.children}</FakeTransition> : null,
)
return {CSSTransition: FakeCSSTransition, Transition: FakeTransition}
})
test('you can mock things with jest.mock', () => {
const {queryByTestId} = render(<HiddenMessage initialShow={true} />)
expect(queryByTestId('hidden-message')).toBeTruthy()
Simulate.click(queryByTestId('toggle-message'))
expect(queryByTestId('hidden-message')).toBeFalsy()
})
Note that because they're Jest mock functions (jest.fn()
), you could also make
assertions on those as well if you wanted.
Open full test
for the full example.
This looks like more work that shallow rendering (and it is), but it gives you
more confidence so long as your mock resembles the thing you're mocking closly
enough.
If you want to make things more like shallow rendering, then you could do
something more
like this.
Learn more about how Jest mocks work from my blog post:
"But really, what is a JavaScript mock?"
I don't want to use data-test
attributes for everything. Do I have to?
Definitely not. That said, a common reason people don't like the data-test
attribute is they're concerned about shipping that to production. I'd suggest
that you probably want some simple E2E tests that run in production on occasion
to make certain that things are working smoothly. In that case the data-test
attributes will be very useful. Even if you don't run these in production, you
may want to run some E2E tests that run on the same code you're about to ship to
production. In that case, the data-test
attributes will be valuable there as
well.
All that said, if you really don't want to ship data-test
attributes, then you
can use
this simple babel plugin
to remove them.
If you don't want to use them at all, then you can simply use regular DOM
methods and properties to query elements off your container.
const firstLiInDiv = container.querySelector('div li')
const allLisInDiv = container.querySelectorAll('div li')
const rootElement = container.firstChild
Other Solutions
In preparing this project,
I tweeted about it
and
Sune Simonsen
took up the challenge.
We had different ideas of what to include in the library, so I decided to create
this one instead.
Guiding Principles
The less your tests resemble the way your software is used, the less confidence they can give you.
We try to only expose methods and utilities that encourage you to write tests
that closely resemble how your react components are used.
Utilities are included in this project based on the following guiding
principles:
- If it relates to rendering components, it deals with DOM nodes rather than
component instances, nor should it encourage dealing with component
instances.
- It should be generally useful for testing individual React components or
full React applications. While this library is focused on
react-dom
,
utilities could be included even if they don't directly relate to
react-dom
.
- Utility implementations and APIs should be simple and flexible.
At the end of the day, what we want is for this library to be pretty
light-weight, simple, and understandable.
Contributors
Thanks goes to these people (emoji key):
This project follows the all-contributors specification.
Contributions of any kind welcome!
LICENSE
MIT