DEV Community

Alex Spinov
Alex Spinov

Posted on

Testing Library Has a Free API That Tests Components the Way Users Actually Use Them

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");
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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..."));
});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

Test your data-driven UIs? My Apify tools provide test data for your components.

Custom testing setup? Email spinov001@gmail.com

Top comments (0)