-
Notifications
You must be signed in to change notification settings - Fork 5
Introduction to the Testing Library
Ben Blazke edited this page Dec 1, 2020
·
10 revisions
The @testing-library family of packages helps you test UI components in a user-centric way.
The more your tests resemble the way your software is used, the more confidence they can give you.
--- from https://testing-library.com/docs/ and https://kentcdodds.com/about/
The Testing Library comes in many flavors. The following three are relevant for our workshop:
- provides utilities for querying the DOM the same way a user would:
getBy*,getAllBy*,queryBy*,queryAllBy*,findBy*,findAllBy*
- adds API on top of DOM Testing Library:
render,cleanup,act
- adds the familiar API to the
cy.command of Cypress
Let's assume you want to test the following React component:
import React from 'react'
function FavoriteNumber({min = 1, max = 9}) {
const [number, setNumber] = React.useState(0)
const [numberEntered, setNumberEntered] = React.useState(false)
function handleChange(event) {
setNumber(Number(event.target.value))
setNumberEntered(true)
}
const isValid = !numberEntered || (number >= min && number <= max)
return (
<div>
<label htmlFor="favorite-number">Favorite Number</label>
<input
id="favorite-number"
type="number"
value={number}
onChange={handleChange}
/>
{isValid ? null : <div role="alert">The number is invalid</div>}
</div>
)
}
export {FavoriteNumber}Here is what it would render:
<div><label for="favorite-number">Favorite Number</label><input id="favorite-number" type="number" value="0"></div>Try to write the test yourself!
import React from 'react'
import {render} from '@testing-library/react'
import {FavoriteNumber} from '../favorite-number'
test('renders a number input with a label "Favorite Number"', () => {
const {getByLabelText} = render(<FavoriteNumber />)
const input = getByLabelText(/favorite number/i)
expect(input).toHaveAttribute('type', 'number')
})To test the dynamic behavior you can use fireEvent:
import {render, fireEvent} from '@testing-library/react'
...
fireEvent.change(input, {target: {value: '10'}})
expect(getByRole('alert')).toHaveTextContent(/the number is invalid/i)Or consider using the newer @testing-library/user-event library:
import * as userEvent from '@testing-library/user-event'
...
userEvent.type(input, 'hello world')Check for accessibility violations:
import {axe, toHaveNoViolations} from 'jest-axe'
import 'jest-axe/extend-expect'
...
const {container} = render(<FavoriteNumber />)
const results = await axe(container)
expect(results).toHaveNoViolations() // or expect(results.violations).toHaveLength(0)Mosts components will be more complicated and some will render data coming from API endpoints. Here is an example of mocking the data:
import {loadGreeting as mockLoadGreeting} from '../api'
test('loads greetings on click', () => {
mockLoadGreeting.mockResolvedValueOnce({data: {greeting: 'TEST_GREETING'}})
const {getByLabelText, getByText} = render(<GreetingLoader />)
const nameInput = getByLabelText(/name/i)
const loadButton = getByText(/load/i)
nameInput.value = 'Mary'
fireEvent.click(loadButton)
})- learn the differences between the DOM, React and Cypress Testing Library
- see a more complete example from the RTL documentation
- learn which query to use
- avoid the most common mistakes with RTL