React Testing with react-testing-library

Hi, first of all I’d like to thank you for all your support for my React Router Testing with Jest and enzyme article that reached over 100+ fans and 600+ claps! It’s been really meaningful to me and motivated me to write new article about testing.

So this time I’d like to review the recently popular testing library: react-testing-library by Kent C. Dodds.

From its README:

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.

I recently used react-testing-library in one of my OSS project (react-save-localstorage) and I think it’s good, simple to use, and works seamlessly with Jest.

Testing simple React component

Let’s say we have a simple component:

import React from 'react'
const Hello = ({ name }) => <div>{name}</div>
export default Hello

I’d like to test that the name renders correctly, so let’s write the test:

import { render, cleanup } from 'react-testing-library'
import Hello from './Hello'
afterEach(cleanup)
describe('Hello', () => { 
it('should contains name', () => {
const { getByText } = render(<Hello name="Antony" />)
getByText('Antony')
})
})

Let’s review one by one:

  • cleanup: Unmounts React trees that were mounted with render.
  • render: Render into a container which is appended to document.body
  • getByText will search for all elements that have a text node with textContent matching the given TextMatch

Let’s change name="Antony" into name="Whoops" and you’ll get an error telling that the element cannot be found:

Unable to find an element with the text: Whoops

Testing props change

Testing props change is also simple, you can use rerender utils after rendering once.

import { render, cleanup } from 'react-testing-library'
import Hello from './Hello'
afterEach(cleanup)
describe('Hello', () => { 
it('should contains name with prop change', () => {
const { getByText, rerender } = render(<Hello name="Antony" />)
getByText('Antony')
rerender(<Hello name="Budi" />)
getByText('Budi')
})
})

Testing events

You can also test events like clicking element using fireEvent util.

import { render, cleanup, fireEvent } from 'react-testing-library'
import Hello from './Hello'
afterEach(cleanup)
describe('Hello', () => { 
it('should click on text', () => {
const { getByText } = render(<Hello name="Antony" />)
const textEl = getByText('Antony')
fireEvent.click(textEl)
})
})

Testing async rendering

You can also wait for an element to resolve promise and check its output using waitForElement:

import { 
render,
cleanup,
waitForElement
} from 'react-testing-library'
import Hello from './Hello'
afterEach(cleanup)
describe('Hello', () => { 
it('should contains name', async () => {
const { getByText } = render(<Hello name="Antony" />)
await waitForElement(() => getByText('Antony'))
})
})

Debugging experience

This is one reason why I love react-testing-library, when there’s failed expectation, it’ll print a nice DOM structure so you can debug clearly:

Unable to find an element with the text: Whoops. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
<body>
<div>
<div>
Budi
</div>
</div>
</body>

You can also trigger this manually by calling debug after render:

const { debug } = render(<Hello name="Antony" />)
debug()

Okay I think that’s all from me, actually there’s still a lot of APIs to cover but I think it’s a good introduction to react-test-library. I’ll probably update the article if I find some good APIs. I hope it’s useful for you all.

If you like the article, please share with your friends and don’t forget to clap! Look forward for the next article. ❤