Skip to content

Introduction to the Testing Library

Ben Blazke edited this page Nov 30, 2020 · 10 revisions

What is the Testing Library

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/

Flavors

DOM Testing Library

  • provides utilities for querying the DOM the same way a user would: getBy*, getAllBy*, queryBy*, queryAllBy*, findBy*, findAllBy*

React Testing Library

  • adds API on top of DOM Testing Library: render, cleanup, act

Cypress Testing Library

  • adds the familiar API to the cy. command of Cypress

Simple React component

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>

Thinking about testing

A simple small or unit test using RTL

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)

Mocking API requests

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)
})

Next steps

Clone this wiki locally