React Testing Library (RTL) is a UI-layer testing framework that helps us ensure that React components are rendering and behaving as expected.
The main advantages of RTL over other UI-layer testing frameworks are:
The logic behind this is that a user will not care about the implementation details of a React component such as the component’s state, the props passed to it, etc. The user will only care about whether or not they are able to use the app.
//Component to be tested TodoList.js
const TodoList = () => {
return (
<div>
<h1>Todo List</h1>
<ul>
<li>
<label htmlFor="item1">Read all the Sunday newspapers</label>
<input type="checkbox" id="item1" />
</li>
<li>
<label htmlFor="item2">Mow the lawn</label>
<input type="checkbox" id="item2" />
</li>
<li>
<label htmlFor="item3">Wash the car</label>
<input type="checkbox" id="item3" />
</li>
</ul>
</div>
);
};
export default TodoList;
The React Testing Library exposes two essential values render()
and screen()
.
Render - This function is used to virtually render components by taking JSX as an argument.
Screen - This is a special object that represents the browser window.
//Component test file TodoList.test.js
import { render, screen } from "@testing-library/react";
// import todo component to be tested
import TodoList from "./components/TodoList";
// simulate user events such as click and hover
import userEvent from "@testing-library/user-event";
// import Jest library for assertions
import "@testing-library/jest-dom";
test("should mark the first checkbox as checked", () => {
// virtually render the Todo list
render(<TodoList />);
// grab the todo item
const todoItem = screen.getByLabelText("Read all the Sunday newspapers");
// simulate a "click" on the todo completed checkbox
userEvent.click(todoItem);
// assert that the todo item checkbox was checked
expect(todoItem).toBeChecked();
});
getByText()
, getByRole()
The Screen object exposes a number of useful query methods. getByText
and getByRole
are two such methods.
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
const Button = () => {
return (
<button type="submit" disabled>
Submit
</button>
);
};
test('A "Submit" button is rendered', () => {
// Render the Button component
render(<Button />);
// Extract the <button>Submit</button> node
const button = screen.getByText("Submit");
// simple test example for brevity
expect(button).toBeInDocument();
});
// Alternative button node query
test("extracts the button DOM node", () => {
// Render the Button component
render(<Button />);
// Extract the <button>Submit</button> node
const button = screen.getByRole("button");
// simple test example for brevity
expect(button).toBeInDocument();
});
The ability to query DOM nodes allows us to be able to test them with Jest assertions
import { render } from "@testing-library/react";
import "@testing-library/jest-dom";
const Button = () => {
return (
<button type="submit" disabled>
Submit
</button>
);
};
test("should show the button as disabled", () => {
// render Button component
render(<Button />);
// Extract <button>Submit</button> Node
const button = screen.getByRole("button");
// Assert button is disabled
expect(button).toBeDisabled();
});
In the 2nd part of this RTL series, we will focus on asynchronous testing and how to handle when nodes are added and removed from the DOM.
RTL part 2 - Asynchronous testing with React testing library »
💻 Software engineer. React enthusiast. Elm and Elixir curious 💻