React Testing Library

itechmeat/llm-code/skills/react-testing-library

by itechmeat7ae8a005245770a0fa1e10337b2eb10174c5886bNo licenseListed Oct 9, 2026Updated Oct 9, 2026

React Testing Library: user-centric component testing with queries, user-event simulation, async utilities, and accessibility-first API. Use when writing React component tests, selecting elements by role/label/text, simulating user events, or testing async UI behavior. Keywords: React Testing Library, @testing-library/react, user-event, queries, render.

Instructions onlySoftware Development
AI-generated overview

Reference guide for writing React component tests with React Testing Library, covering queries, user events, async utilities and configuration.

What it does
This skill supplies instructions and reference material for testing React components with React Testing Library. It explains query selection and priority, user-event simulation, async helpers such as waitFor and findBy, hook testing, custom render wrappers, debugging output, jest-dom matchers and global configuration. It also lists anti-patterns and best practices, and points to six reference documents on queries, user events, API, async, debugging and config.
When to use it
Use it when writing or reviewing React component tests, especially when selecting elements by role, label or text, simulating user interactions, or testing asynchronous UI behavior. It is also relevant when setting up testing utilities such as custom render wrappers or renderHook.
Requirements
Instructions only; no scripts are shipped. Following the guidance assumes a JavaScript/TypeScript project with React and the @testing-library/react and @testing-library/dom packages installed, optionally with @testing-library/user-event and @testing-library/jest-dom, plus a test runner such as Jest.

React Testing Library Skill

Quick Navigation

TopicLink
Queriesreferences/queries.md [blocked]
User Eventsreferences/user-events.md [blocked]
APIreferences/api.md [blocked]
Asyncreferences/async.md [blocked]
Debuggingreferences/debugging.md [blocked]
Configreferences/config.md [blocked]

Installation

Install: npm install --save-dev @testing-library/react @testing-library/dom. Recommended extras: @testing-library/user-event and @testing-library/jest-dom. React 19 requires v16.1.0+.

Core Philosophy

"The more your tests resemble the way your software is used, the more confidence they can give you."

Avoid testing:

  • Internal state of components
  • Internal methods
  • Lifecycle methods
  • Child component implementation details

Test instead:

  • What users see and interact with
  • Behavior from user's perspective
  • Accessibility (queries by role, label)

Query Priority

Use queries in this order of preference:

1. Accessible to Everyone (Preferred)

ts
// Best — by ARIA rolegetByRole("button", { name: /submit/i });getByRole("textbox", { name: /email/i });
// Form fields — by labelgetByLabelText("Email");
// Non-interactive content — by textgetByText("Welcome back!");

2. Semantic Queries

ts
// ImagesgetByAltText("Company logo");
// Title attribute (less reliable)getByTitle("Close");

3. Test IDs (Escape Hatch)

ts
// Only when other queries don't workgetByTestId("custom-element");

Query Types

TypeNo Match1 Match>1 MatchAsync
getBy...throwreturnthrowNo
queryBy...nullreturnthrowNo
findBy...throwreturnthrowYes
getAllBy...throwarrayarrayNo
queryAllBy...[]arrayarrayNo
findAllBy...throwarrayarrayYes

When to use:

  • getBy* — element exists
  • queryBy* — element may not exist (assertions like expect(...).not.toBeInTheDocument())
  • findBy* — element appears asynchronously

Basic Test Pattern

tsx
import { render, screen } from "@testing-library/react";import userEvent from "@testing-library/user-event";
test("shows greeting after login", async () => {  const user = userEvent.setup();  render(<App />);
  // Act — simulate user interactions  await user.type(screen.getByLabelText(/username/i), "john");  await user.click(screen.getByRole("button", { name: /login/i }));
  // Assert — verify outcome  expect(await screen.findByText(/welcome, john/i)).toBeInTheDocument();});

User Events

Always use @testing-library/user-event over fireEvent:

ts
import userEvent from "@testing-library/user-event";
test("user interactions", async () => {  const user = userEvent.setup();
  // Click  await user.click(element);  await user.dblClick(element);  await user.tripleClick(element);
  // Type  await user.type(input, "Hello");  await user.clear(input);
  // Select  await user.selectOptions(select, ["option1", "option2"]);
  // Keyboard  await user.keyboard("{Enter}");  await user.keyboard("[ShiftLeft>]a[/ShiftLeft]"); // Shift+A
  // Clipboard  await user.copy();  await user.paste();
  // Pointer  await user.hover(element);  await user.unhover(element);});

Async Patterns

waitFor — Retry Until Success

ts
await waitFor(() => {  expect(screen.getByText("Loaded")).toBeInTheDocument();});
// With optionsawait waitFor(() => expect(callback).toHaveBeenCalled(), {  timeout: 5000,  interval: 100,});

findBy — Built-in waitFor

ts
// Equivalent to: await waitFor(() => getByText('Loaded'))const element = await screen.findByText("Loaded");

waitForElementToBeRemoved

ts
await waitForElementToBeRemoved(() => screen.queryByText("Loading..."));

Common Patterns

Custom Render with Providers

tsx
// test-utils.tsximport { render } from "@testing-library/react";import { ThemeProvider } from "./ThemeProvider";import { AuthProvider } from "./AuthProvider";
function AllProviders({ children }) {  return (    <ThemeProvider>      <AuthProvider>{children}</AuthProvider>    </ThemeProvider>  );}
const customRender = (ui, options) => render(ui, { wrapper: AllProviders, ...options });
export * from "@testing-library/react";export { customRender as render };

Testing Hooks

ts
import { renderHook, act } from "@testing-library/react";
test("useCounter increments", () => {  const { result } = renderHook(() => useCounter());
  expect(result.current.count).toBe(0);
  act(() => {    result.current.increment();  });
  expect(result.current.count).toBe(1);});

Rerender with New Props

ts
const { rerender } = render(<Counter count={1} />);expect(screen.getByText("Count: 1")).toBeInTheDocument();
rerender(<Counter count={2} />);expect(screen.getByText("Count: 2")).toBeInTheDocument();

Query Within Container

ts
import { within } from "@testing-library/react";
const modal = screen.getByRole("dialog");const submitBtn = within(modal).getByRole("button", { name: /submit/i });

Debugging

ts
// Print entire DOMscreen.debug();
// Print specific elementscreen.debug(screen.getByRole("button"));
// Log available rolesimport { logRoles } from "@testing-library/react";logRoles(container);
// With prettyDOM optionsscreen.debug(undefined, 10000); // max length

jest-dom Matchers

ts
import "@testing-library/jest-dom";
expect(element).toBeInTheDocument();expect(element).toBeVisible();expect(element).toBeEnabled();expect(element).toBeDisabled();expect(element).toHaveTextContent("Hello");expect(element).toHaveValue("input value");expect(element).toHaveAttribute("href", "/home");expect(element).toHaveClass("active");expect(element).toHaveFocus();expect(element).toBeChecked();

Configuration

ts
import { configure } from "@testing-library/react";
configure({  // Custom test ID attribute  testIdAttribute: "data-my-test-id",
  // Async timeout  asyncUtilTimeout: 5000,
  // Default hidden  defaultHidden: true,
  // Throw suggestions (debugging)  throwSuggestions: true,});

❌ Prohibitions (Anti-patterns)

ts
// ❌ Don't query by class/idcontainer.querySelector(".my-class");
// ❌ Don't use container.firstChildconst { container } = render(<Component />);expect(container.firstChild).toHaveClass("active");
// ❌ Don't use fireEvent when userEvent worksfireEvent.click(button); // Use userEvent.click instead
// ❌ Don't test implementation detailsexpect(component.state.loading).toBe(false);
// ❌ Don't use waitFor with findByawait waitFor(() => screen.findByText("x")); // findBy already waits
// ❌ Don't assert inside waitFor callback (unless necessary)await waitFor(() => {  expect(mockFn).toHaveBeenCalled(); // OK - need to wait for call});

✅ Best Practices

ts
// ✅ Use screen for all queriesimport { render, screen } from "@testing-library/react";render(<Component />);screen.getByRole("button"); // Good
// ✅ Prefer userEvent over fireEventconst user = userEvent.setup();await user.click(button);
// ✅ Use findBy for async elementsconst element = await screen.findByText("Loaded");
// ✅ Use queryBy for non-existence assertionsexpect(screen.queryByText("Error")).not.toBeInTheDocument();
// ✅ Use within for scoped queriesconst form = screen.getByRole("form");within(form).getByLabelText("Email");
// ✅ Use accessible queries (role, label, text)getByRole("button", { name: /submit/i });

TextMatch Options

ts
// Exact match (default)getByText("Hello World");
// Substring matchgetByText("llo Worl", { exact: false });
// RegexgetByText(/hello world/i);
// Custom functiongetByText((content, element) => {  return element.tagName === "SPAN" && content.startsWith("Hello");});

Quick Reference

ImportUsage
renderRender component to DOM
screenQuery the rendered DOM
cleanupUnmount components (auto in Jest)
actWrap state updates
renderHookTest custom hooks
withinScope queries to element
waitForRetry until assertion passes
configureSet global options
userEvent.setup()Create user event instance

Links

Source and attribution

Source:itechmeat/llm-codeinskills/react-testing-libraryat commit7ae8a00

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal