Jagat Jeevan Sahoo
UI Developer @ThoughtWorks A tech enthusiast
Test JavaScript the way user would interact with the component
This is a very lightweight solution for testing React components. It is built on top of “react-dom” and “react-dom/test-utils” and provides light utility functions to encourage better testing practice.
Created by : Kent C. Dodds
The guiding principle is
npm install --save-dev @testing-library/react
Using npm
Using yarn
yarn add --dev @testing-library/react
import React from 'react';
const title = "Hello World";
function App() {
return <h1>{title}</h1>
}
export default App;
Component Code
import React from 'react';
import {
render,
screen
} from '@testing-library/react';
import App from './App';
describe('App', () => {
test("renders the App component", () => {
render(<App />);
screen.debug();
});
});
Test File
screen.debug() would return the whole DOM to the terminal
import React from 'react';
const title = "Hello World";
function App() {
return (
<div>
<label for="search">Search:</label>
<input type="text" id="search" />
</div>
);
}
export default App;
Component Code
import {render, screen} from '@testing-library/react';
... All the import statements
describe('App', () => {
test("Find the search text", () => {
render(<app />);
expect(screen.getByText(/Search:/)).toBeInTheDocument();
expect(screen.getByRole('textbox')).toBeInTheDocument();
});
});
Test File
In order to assert, we need to select elements first. There are 3 ways :
Example:
getByText / queryByText / findByText
getByRole / queryByRole / findByRole
Below are the ways all the 3 variants would apply
For selecting multiple elements in the DOM, we can use any of getAllBy, queryAllBy, findAllBy.
It would return an array of elements
All the assertion are from jest. However, RTL extends from jest to give more meaningful assertion. Full list here. Below are some list.
RTL has 'fireEvent' to trigger user interaction. FireEvent takes a node and the event.
import { screen, render, fireEvent } from '@testing-library/react';
... All the other imports goes here
describe("testing the earlier label and text", () => {
test("expect the input to be in the document", () => {
render(<App />);
expect(screen.queryByLabelText(/some label/)).toBeInTheDocument();
expect(screen.queryByPlaceholderText(/search here/)).toBeInTheDocument();
expect(screen.getByRole(/Search Button/)).toBeDisabled();
fireEvent.change(screen.getByRole('textbox'), {
target: { value: "the changed text" }
});
expect(screen.getByRole(/Search Button/)).toBeEnabled();
});
});
fireEvent(node: HTMLElement, event: Event);
Or
fireEvent(EventName)(HTMLElement, EventProperties);
Find the full set of events that can be used are here.
user-event is a companion library for Testing Library that provides more advanced simulation of browser interactions than the built-in fireEvent method.
npm install --save-dev @testing-library/user-event
// or
yarn add @testing-library/user-event --dev
import React from 'react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('click', () => {
render(
<div>
<label htmlFor="checkbox">Check</label>
<input id="checkbox" type="checkbox" />
</div>
)
userEvent.click(screen.getByText('Check'))
expect(screen.getByLabelText('Check')).toBeChecked()
})
To get a reference of the event triggered, use 'createEvent'.
const myEvent = createEvent.click(node, { button: 2 })
fireEvent(node, myEvent)
// myEvent.timeStamp can be accessed just like any other properties from myEvent
Creating a generic event
// simulate the 'input' event on a file input
fireEvent(
input,
createEvent('input', input, {
target: { files: inputFiles },
...init,
})
)
It might not be required to test the full functionality and just mock the events.
import axios from 'axios';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
...Other imports
jest.mock('axios');
describe('App', () => {
test('fetches stories from an API and displays them', async () => {
const stories = [
{ objectID: '1', title: 'Hello' },
];
axios.get.mockImplementationOnce(() =>
Promise.resolve({ data: { hits: stories } })
);
render(<App />);
await userEvent.click(screen.getByRole('button'));
const items = await screen.findAllByRole('listitem');
expect(items).toHaveLength(1);
});
});
By Jagat Jeevan Sahoo
To write the test, the use would use.