К основному содержанию
скилл CX Codex

react-testing-library · itechmeat — скилл для Claude Code: установка и суть

Скилл учит ИИ писать тесты React-компонентов глазами пользователя: через ARIA-роли, user-event и асинхронные проверки.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add itechmeat/llm-code --skill react-testing-library -a claude-code
Открыть на GitHub

Что делает react-testing-library

Скилл задаёт правила тестирования React-компонентов без привязки к внутреннему состоянию. Главный приоритет отдан доступности интерфейса. Агент сначала ищет элементы по ARIA-ролям через getByRole или подписям полей, а селектор getByTestId оставляет для крайних случаев. Действия пользователя эмулируются пакетом @testing-library/user-event вместо fireEvent.

В руководстве описаны обёртки с провайдерами и тестирование кастомных хуков через renderHook с вызовом act. Для работы с задержками предложены утилиты waitFor и запросы группы findBy*. Отладка дерева выводится функциями screen.debug() или logRoles(container).

Если проект переходит на React 19, библиотека должна быть не старее 16.1.0, иначе окружение упадёт с ошибкой. Потребуются установленные пакеты @testing-library/react и @testing-library/dom.

Какие задачи решает

  • написать тесты взаимодействия с формой через userEvent
  • дождаться появления асинхронного контента с помощью findBy и waitFor
  • протестировать кастомный хук с помощью renderHook и act
  • настроить кастомный render с провайдерами контекста

Примеры запросов агенту

  • Напиши тест формы логина с заполнением полей через userEvent и поиском по getByRole
  • Покрой тестами хук useCounter через renderHook и act
  • Покажи, как написать тест с ожиданием асинхронного исчезновения лоадера

Как установить react-testing-library

1Через Skills CLI (Vercel)

npx skills add itechmeat/llm-code --skill react-testing-library -a claude-code

2Вручную: положить файлы

~/.claude/skills/react-testing-library/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/react-testing-library/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора2

Другие версии react-testing-library

Похожие

react-testing affaan-m Скилл для написания тестов React-компонентов, хуков и страниц: React Testing Library, Vitest или Jest, MSW для моков сети и проверки доступности через axe. CX Codex +1 4 800установок jest-react-testing manutej Скилл для тестирования React-компонентов и хуков через Jest и React Testing Library: конфигурация, моки, асинхронные сценарии. CX Codex +1 1 200установок appbuilder-testing adobe Скилл генерирует и запускает Jest-тесты для экшенов Adobe App Builder и React-компонентов, включая моки Adobe SDK. CX Codex +1 714установок react-specialist VoltAgent Community Субагент для разбора и доработки React-кода: производительности, состояния, архитектуры и тестов. Подходит, когда проект уже есть и нужны точечные изменения. 815установок react-expert reactjs Исследует API и концепции React по исходникам, тестам и пуллреквестам фреймворка. Помогает собрать точные примеры без устаревших галлюцинаций модели. CX Codex +1 1 300установок react-native-testing callstack Скилл для написания и проверки тестов компонентов React Native с помощью Testing Library. Покрывает запросы, взаимодействия, асинхронные паттерны и работает с версиями v13 (React 18) и v14 (React 19+). CX Codex +1 5 300установок

Рядом в теме «Тесты и QA»

Всё по теме «Тесты и QA» →

Вопросы о react-testing-library

Как установить react-testing-library в Claude Code?

Выполните команду npx skills add itechmeat/llm-code --skill react-testing-library -a claude-code в терминале.

Что делает скилл react-testing-library?

Он направляет модель писать компонентные тесты с точки зрения пользователя: выбирать элементы по ролям и меткам, эмулировать события через userEvent и тестировать асинхронное поведение.

С какой версии React совместим скилл?

Для работы с React 19 требуется версия @testing-library/react 16.1.0 или выше, а также пакет @testing-library/dom в зависимостях проекта.

Исходный текст

SKILL.md · MIT
---
name: react-testing-library
description: "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."
metadata:
  version: "16.3.3"
  release_date: "2026-09-04"
---

# React Testing Library Skill

## Quick Navigation

| Topic       | Link                                                   |
| ----------- | ------------------------------------------------------ |
| Queries     | [references/queries.md](references/queries.md)         |
| User Events | [references/user-events.md](references/user-events.md) |
| API         | [references/api.md](references/api.md)                 |
| Async       | [references/async.md](references/async.md)             |
| Debugging   | [references/debugging.md](references/debugging.md)     |
| Config      | [references/config.md](references/config.md)           |

---

## 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 role
getByRole("button", { name: /submit/i });
getByRole("textbox", { name: /email/i });

// Form fields — by label
getByLabelText("Email");

// Non-interactive content — by text
getByText("Welcome back!");
```

### 2. Semantic Queries

```ts
// Images
getByAltText("Company logo");

// Title attribute (less reliable)
getByTitle("Close");
```

### 3. Test IDs (Escape Hatch)

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

---

## Query Types

| Type            | No Match | 1 Match | >1 Match | Async |
| --------------- | -------- | ------- | -------- | ----- |
| `getBy...`      | throw    | return  | throw    | No    |
| `queryBy...`    | null     | return  | throw    | No    |
| `findBy...`     | throw    | return  | throw    | Yes   |
| `getAllBy...`   | throw    | array   | array    | No    |
| `queryAllBy...` | []       | array   | array    | No    |
| `findAllBy...`  | throw    | array   | array    | Yes   |

**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 options
await 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.tsx
import { 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 DOM
screen.debug();

// Print specific element
screen.debug(screen.getByRole("button"));

// Log available roles
import { logRoles } from "@testing-library/react";
logRoles(container);

// With prettyDOM options
screen.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/id
container.querySelector(".my-class");

// ❌ Don't use container.firstChild
const { container } = render(<Component />);
expect(container.firstChild).toHaveClass("active");

// ❌ Don't use fireEvent when userEvent works
fireEvent.click(button); // Use userEvent.click instead

// ❌ Don't test implementation details
expect(component.state.loading).toBe(false);

// ❌ Don't use waitFor with findBy
await 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 queries
import { render, screen } from "@testing-library/react";
render(<Component />);
screen.getByRole("button"); // Good

// ✅ Prefer userEvent over fireEvent
const user = userEvent.setup();
await user.click(button);

// ✅ Use findBy for async elements
const element = await screen.findByText("Loaded");

// ✅ Use queryBy for non-existence assertions
expect(screen.queryByText("Error")).not.toBeInTheDocument();

// ✅ Use within for scoped queries
const 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 match
getByText("llo Worl", { exact: false });

// Regex
getByText(/hello world/i);

// Custom function
getByText((content, element) => {
  return element.tagName === "SPAN" && content.startsWith("Hello");
});
```

---

## Quick Reference

| Import              | Usage                             |
| ------------------- | --------------------------------- |
| `render`            | Render component to DOM           |
| `screen`            | Query the rendered DOM            |
| `cleanup`           | Unmount components (auto in Jest) |
| `act`               | Wrap state updates                |
| `renderHook`        | Test custom hooks                 |
| `within`            | Scope queries to element          |
| `waitFor`           | Retry until assertion passes      |
| `configure`         | Set global options                |
| `userEvent.setup()` | Create user event instance        |

## Links

- [Documentation](https://testing-library.com/docs/react-testing-library/intro/)
- [Releases](https://github.com/testing-library/react-testing-library/releases)
- [GitHub](https://github.com/testing-library/react-testing-library)
- [npm](https://www.npmjs.com/package/@testing-library/react)

© itechmeat · MIT · itechmeat/llm-code

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой