Testing Library is the testing utility that encourages you to test behavior, not implementation. Query by role, text, label — the way users find elements.
The Query Priority
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
// 1. getByRole — accessible name (BEST)
screen.getByRole("button", { name: "Submit" });
screen.getByRole("heading", { level: 2 });
screen.getByRole("textbox", { name: "Email" });
screen.getByRole("checkbox", { name: "Accept terms" });
// 2. getByLabelText — form elements
screen.getByLabelText("Email address");
// 3. getByText — non-interactive elements
screen.getByText("Welcome back!");
screen.getByText(/no products found/i);
// 4. getByTestId — LAST RESORT
screen.getByTestId("custom-component");
User Events: Real Interactions
const user = userEvent.setup();
test("form submission", async () => {
render(<SignUpForm />);
await user.type(screen.getByLabelText("Email"), "test@example.com");
await user.type(screen.getByLabelText("Password"), "securepass123");
await user.click(screen.getByRole("checkbox", { name: "Accept terms" }));
await user.click(screen.getByRole("button", { name: "Sign Up" }));
expect(screen.getByText("Account created!")).toBeInTheDocument();
});
test("keyboard navigation", async () => {
render(<DropdownMenu />);
await user.tab(); // Focus first item
await user.keyboard("{Enter}"); // Open menu
await user.keyboard("{ArrowDown}"); // Navigate
await user.keyboard("{Enter}"); // Select
expect(screen.getByText("Option selected")).toBeInTheDocument();
});
Async Queries: Wait for Elements
test("loads data asynchronously", async () => {
render(<ProductList />);
// findBy waits for element to appear
const products = await screen.findAllByRole("listitem");
expect(products).toHaveLength(5);
// waitFor — custom assertions
await waitFor(() => {
expect(screen.getByText("$29.99")).toBeInTheDocument();
});
// waitForElementToBeRemoved
render(<LoadingComponent />);
await waitForElementToBeRemoved(() => screen.queryByText("Loading..."));
});
within: Scoped Queries
test("products in categories", () => {
render(<ProductGrid />);
const electronics = screen.getByRole("region", { name: "Electronics" });
const clothing = screen.getByRole("region", { name: "Clothing" });
expect(within(electronics).getAllByRole("article")).toHaveLength(3);
expect(within(clothing).getAllByRole("article")).toHaveLength(5);
});
Custom Matchers (jest-dom)
import "@testing-library/jest-dom";
expect(screen.getByRole("button")).toBeEnabled();
expect(screen.getByRole("button")).toBeDisabled();
expect(screen.getByRole("textbox")).toHaveValue("hello");
expect(screen.getByText("Error")).toHaveClass("text-red");
expect(screen.getByRole("alert")).toHaveTextContent(/error/i);
expect(screen.getByLabelText("Email")).toBeRequired();
expect(screen.getByRole("link")).toHaveAttribute("href", "/about");
Test your data-driven UIs? My Apify tools provide test data for your components.
Custom testing setup? Email spinov001@gmail.com
Top comments (0)