Common mistakes with React Testing Library
Not using Testing Library ESLint pluginsβ
Importance: medium
Advice: Install and use the ESLint plugin for Testing Library.
Using wrapper as the variable name for the return value from renderβ
Importance: low
Advice: Destructure what you need from render or call it view.
// β
const wrapper = render(<Example prop="1" />);
wrapper.rerender(<Example prop="2" />);
// β
const { rerender } = render(<Example prop="1" />);
rerender(<Example prop="2" />);
Using cleanupβ
Importance: medium Advice: Don't use cleanup as it happens automatically now.
// β
import {render, screen, cleanup} from '@testing-library/react'
afterEach(cleanup)
// β
import {render, screen} from '@testing-library/react'
Not using screenβ
Importance: medium
Advice: Use screen for querying and debugging.
// β
const {getByRole} = render(<Example />)
const errorMessageNode = getByRole('alert')
// β
render(<Example />)
const errorMessageNode = screen.getByRole('alert')
Using the wrong assertionβ
Importance: high
Advice: Install and use @testing-library/jest-dom for better error messages.
const button = screen.getByRole('button', {name: /disabled button/i})
// β
expect(button.disabled).toBe(true)
// β
expect(button).toBeDisabled()
Wrapping things in act unnecessarilyβ
Importance: medium
Advice: Learn when act is necessary and avoid unnecessary wrapping.
// β
act(() => {
render(<Example />)
})
// β
render(<Example />)
Using the wrong queryβ
Importance: high
Advice: Use appropriate queries based on the DOM structure and accessibility.
// β
screen.getByTestId('username')
// β
screen.getByRole('textbox', {name: /username/i})
Using query variants for anything except checking for non-existence*β
Importance: high
Advice: Use query* variants only for asserting element absence.
// β
expect(screen.queryByRole('alert')).toBeInTheDocument()
// β
expect(screen.getByRole('alert')).toBeInTheDocument()
Using waitFor to wait for elements that can be queried with find*β
Importance: high
Advice: Use find* queries for elements that may not be available immediately.
// β
const submitButton = await waitFor(() =>
screen.getByRole('button', {name: /submit/i}),
)
// β
const submitButton = await screen.findByRole('button', {name: /submit/i})
Passing an empty callback to waitForβ
Importance: high
Advice: Wait for a specific assertion inside waitFor.
// β
await waitFor(() => {})
// β
await waitFor(() => expect(window.fetch).toHaveBeenCalledWith('foo'))
Having multiple assertions in a single waitFor callback
Importance: low
Advice: Only put one assertion in a callback.
// β
await waitFor(() => {
expect(window.fetch).toHaveBeenCalledWith('foo')
expect(window.fetch).toHaveBeenCalledTimes(1)
})
// β
await waitFor(() => expect(window.fetch).toHaveBeenCalledWith('foo'))
expect(window.fetch).toHaveBeenCalledTimes(1)
Performing side-effects in waitForβ
Importance: high
Advice: Put side-effects outside waitFor callbacks.
// β
await waitFor(() => {
fireEvent.keyDown(input, {key: 'ArrowDown'})
expect(screen.getAllByRole('listitem')).toHaveLength(3)
})
// β
fireEvent.keyDown(input, {key: 'ArrowDown'})
await waitFor(() => {
expect(screen.getAllByRole('listitem')).toHaveLength(3)
})
Using get variants as assertions*β
Importance: low
Advice: Make assertions explicit even with get* variants for clarity and debugging.
// β
screen.getByRole('alert', {name: /error/i})
// β
expect(screen.getByRole('alert', {name: /error/i})).toBeInTheDocument()