React Testing Library Best Practices
43 rules across 9 categories for testing React components with Testing Library, prioritized by impact to guide test writing and code review.
Scope
This skill owns React component and hook testing mechanics. For
framework-agnostic questions — which level a behavior belongs at, what a coverage
number means, how to choose test data, how to kill a flake — use
testing-expert.
When to Apply
- Writing new component tests with React Testing Library
- Selecting queries (getByRole, getByLabelText, etc.)
- Handling async operations in tests (findBy, waitFor)
- Simulating user interactions (userEvent)
- Reviewing tests for anti-patterns and implementation detail testing
Rule Categories by Priority
| Priority |
Category |
Impact |
Prefix |
| 1 |
Query Selection |
CRITICAL |
query- |
| 2 |
Async Handling |
CRITICAL |
async- |
| 3 |
Common Anti-Patterns |
CRITICAL |
anti- |
| 4 |
User Interaction |
HIGH |
user- |
| 5 |
Assertions |
HIGH |
assert- |
| 6 |
Component Setup |
MEDIUM |
setup- |
| 7 |
Test Structure |
MEDIUM |
struct- |
| 8 |
Debugging |
LOW-MEDIUM |
debug- |
| 9 |
Accessibility Testing |
LOW |
a11y- |
Quick Reference
1. Query Selection (CRITICAL)
query-prefer-role - Prefer getByRole over other queries
query-avoid-testid - Avoid getByTestId as primary query
query-use-screen - Use screen for queries
query-label-text-forms - Use getByLabelText for form fields
query-role-name-option - Use name option with getByRole
query-get-vs-query - Use getBy for present, queryBy for absent
query-within-scope - Use within() to scope queries
2. Async Handling (CRITICAL)
async-findby-over-waitfor - Use findBy instead of waitFor + getBy
async-await-findby - Always await findBy queries
async-single-assertion-waitfor - Single assertion in waitFor
async-no-side-effects-waitfor - Avoid side effects in waitFor
async-waitfor-disappear - Use waitForElementToBeRemoved
3. Common Anti-Patterns (CRITICAL)
anti-unnecessary-act - Avoid unnecessary act() wrapping
anti-manual-cleanup - Remove manual cleanup calls
anti-implementation-details - Avoid testing implementation details
anti-empty-waitfor - Avoid empty waitFor callbacks
anti-container-queries - Avoid using container for queries
anti-redundant-roles - Avoid adding redundant ARIA roles
4. User Interaction (HIGH)
user-prefer-userevent - Use userEvent over fireEvent
user-setup-before-render - Setup userEvent before render
user-await-interactions - Always await userEvent interactions
user-keyboard-for-special-keys - Use keyboard() for special keys
user-clear-before-type - Use clear() before retyping
5. Assertions (HIGH)
assert-jest-dom-matchers - Use jest-dom matchers
assert-visible-over-in-document - Use toBeVisible() for visibility
assert-text-content - Use toHaveTextContent() for text
assert-have-value - Use toHaveValue() for inputs
assert-accessible-description - Use toHaveAccessibleDescription()
6. Component Setup (MEDIUM)
setup-wrapper-providers - Use wrapper option for providers
setup-custom-render - Create custom render with providers
setup-mock-modules - Mock modules at module level
setup-fake-timers - Configure userEvent with fake timers
setup-render-hook - Use renderHook for testing hooks
7. Test Structure (MEDIUM)
struct-arrange-act-assert - Follow Arrange-Act-Assert pattern
struct-one-behavior-per-test - Test one behavior per test
struct-descriptive-names - Use descriptive test names
struct-avoid-beforeeach-render - Avoid render() in beforeEach
8. Debugging (LOW-MEDIUM)
debug-screen-debug - Use screen.debug() to inspect DOM
debug-logroles - Use logRoles to find available roles
debug-testing-playground - Use Testing Playground for queries
9. Accessibility Testing (LOW)
a11y-role-queries-verify - Role queries verify accessibility
a11y-verify-focus - Test focus management
a11y-test-aria-states - Test ARIA states and properties
How to Use
Read individual reference files for detailed explanations and code examples:
- Section definitions - Category structure and impact levels
- Rule template - Template for adding new rules
Reference Files
| File |
Description |
| references/_sections.md |
Category definitions and ordering |
| assets/templates/_template.md |
Template for new rules |
1---
2name: react-testing-library
3description: React Testing Library mechanics for testing React components and hooks — query selection, async handling, user interaction, assertions, provider setup, and the anti-patterns that make tests brittle. Use for any test that renders a React component or a hook, including choosing between getBy/queryBy/findBy, userEvent flows, waitFor usage, renderHook, custom render wrappers, and reviewing RTL tests for implementation-detail assertions.
4metadata:
5 version: "1.1.0"
6 tags: "react, testing, rtl, components, hooks"
7 author: Ship Shit Dev
8when_to_use: "test a React component, test a React hook, render a component in a test, getByRole, getByLabelText, queryBy vs getBy, findBy, waitFor, userEvent, fireEvent, renderHook, custom render with providers, screen.debug, my component test is brittle, review these RTL tests, testing-library anti-patterns"
9---
10
11# React Testing Library Best Practices
12
1343 rules across 9 categories for testing React components with Testing Library, prioritized by impact to guide test writing and code review.
14
15## Scope
16
17This skill owns React component and hook testing mechanics. For
18framework-agnostic questions — which level a behavior belongs at, what a coverage
19number means, how to choose test data, how to kill a flake — use
20`testing-expert`.
21
22## When to Apply
23
24- Writing new component tests with React Testing Library
25- Selecting queries (getByRole, getByLabelText, etc.)
26- Handling async operations in tests (findBy, waitFor)
27- Simulating user interactions (userEvent)
28- Reviewing tests for anti-patterns and implementation detail testing
29
30## Rule Categories by Priority
31
32| Priority | Category | Impact | Prefix |
33|----------|----------|--------|--------|
34| 1 | Query Selection | CRITICAL | `query-` |
35| 2 | Async Handling | CRITICAL | `async-` |
36| 3 | Common Anti-Patterns | CRITICAL | `anti-` |
37| 4 | User Interaction | HIGH | `user-` |
38| 5 | Assertions | HIGH | `assert-` |
39| 6 | Component Setup | MEDIUM | `setup-` |
40| 7 | Test Structure | MEDIUM | `struct-` |
41| 8 | Debugging | LOW-MEDIUM | `debug-` |
42| 9 | Accessibility Testing | LOW | `a11y-` |
43
44## Quick Reference
45
46### 1. Query Selection (CRITICAL)
47
48- [`query-prefer-role`](references/query-prefer-role.md) - Prefer getByRole over other queries
49- [`query-avoid-testid`](references/query-avoid-testid.md) - Avoid getByTestId as primary query
50- [`query-use-screen`](references/query-use-screen.md) - Use screen for queries
51- [`query-label-text-forms`](references/query-label-text-forms.md) - Use getByLabelText for form fields
52- [`query-role-name-option`](references/query-role-name-option.md) - Use name option with getByRole
53- [`query-get-vs-query`](references/query-get-vs-query.md) - Use getBy for present, queryBy for absent
54- [`query-within-scope`](references/query-within-scope.md) - Use within() to scope queries
55
56### 2. Async Handling (CRITICAL)
57
58- [`async-findby-over-waitfor`](references/async-findby-over-waitfor.md) - Use findBy instead of waitFor + getBy
59- [`async-await-findby`](references/async-await-findby.md) - Always await findBy queries
60- [`async-single-assertion-waitfor`](references/async-single-assertion-waitfor.md) - Single assertion in waitFor
61- [`async-no-side-effects-waitfor`](references/async-no-side-effects-waitfor.md) - Avoid side effects in waitFor
62- [`async-waitfor-disappear`](references/async-waitfor-disappear.md) - Use waitForElementToBeRemoved
63
64### 3. Common Anti-Patterns (CRITICAL)
65
66- [`anti-unnecessary-act`](references/anti-unnecessary-act.md) - Avoid unnecessary act() wrapping
67- [`anti-manual-cleanup`](references/anti-manual-cleanup.md) - Remove manual cleanup calls
68- [`anti-implementation-details`](references/anti-implementation-details.md) - Avoid testing implementation details
69- [`anti-empty-waitfor`](references/anti-empty-waitfor.md) - Avoid empty waitFor callbacks
70- [`anti-container-queries`](references/anti-container-queries.md) - Avoid using container for queries
71- [`anti-redundant-roles`](references/anti-redundant-roles.md) - Avoid adding redundant ARIA roles
72
73### 4. User Interaction (HIGH)
74
75- [`user-prefer-userevent`](references/user-prefer-userevent.md) - Use userEvent over fireEvent
76- [`user-setup-before-render`](references/user-setup-before-render.md) - Setup userEvent before render
77- [`user-await-interactions`](references/user-await-interactions.md) - Always await userEvent interactions
78- [`user-keyboard-for-special-keys`](references/user-keyboard-for-special-keys.md) - Use keyboard() for special keys
79- [`user-clear-before-type`](references/user-clear-before-type.md) - Use clear() before retyping
80
81### 5. Assertions (HIGH)
82
83- [`assert-jest-dom-matchers`](references/assert-jest-dom-matchers.md) - Use jest-dom matchers
84- [`assert-visible-over-in-document`](references/assert-visible-over-in-document.md) - Use toBeVisible() for visibility
85- [`assert-text-content`](references/assert-text-content.md) - Use toHaveTextContent() for text
86- [`assert-have-value`](references/assert-have-value.md) - Use toHaveValue() for inputs
87- [`assert-accessible-description`](references/assert-accessible-description.md) - Use toHaveAccessibleDescription()
88
89### 6. Component Setup (MEDIUM)
90
91- [`setup-wrapper-providers`](references/setup-wrapper-providers.md) - Use wrapper option for providers
92- [`setup-custom-render`](references/setup-custom-render.md) - Create custom render with providers
93- [`setup-mock-modules`](references/setup-mock-modules.md) - Mock modules at module level
94- [`setup-fake-timers`](references/setup-fake-timers.md) - Configure userEvent with fake timers
95- [`setup-render-hook`](references/setup-render-hook.md) - Use renderHook for testing hooks
96
97### 7. Test Structure (MEDIUM)
98
99- [`struct-arrange-act-assert`](references/struct-arrange-act-assert.md) - Follow Arrange-Act-Assert pattern
100- [`struct-one-behavior-per-test`](references/struct-one-behavior-per-test.md) - Test one behavior per test
101- [`struct-descriptive-names`](references/struct-descriptive-names.md) - Use descriptive test names
102- [`struct-avoid-beforeeach-render`](references/struct-avoid-beforeeach-render.md) - Avoid render() in beforeEach
103
104### 8. Debugging (LOW-MEDIUM)
105
106- [`debug-screen-debug`](references/debug-screen-debug.md) - Use screen.debug() to inspect DOM
107- [`debug-logroles`](references/debug-logroles.md) - Use logRoles to find available roles
108- [`debug-testing-playground`](references/debug-testing-playground.md) - Use Testing Playground for queries
109
110### 9. Accessibility Testing (LOW)
111
112- [`a11y-role-queries-verify`](references/a11y-role-queries-verify.md) - Role queries verify accessibility
113- [`a11y-verify-focus`](references/a11y-verify-focus.md) - Test focus management
114- [`a11y-test-aria-states`](references/a11y-test-aria-states.md) - Test ARIA states and properties
115
116## How to Use
117
118Read individual reference files for detailed explanations and code examples:
119
120- [Section definitions](references/_sections.md) - Category structure and impact levels
121- [Rule template](assets/templates/_template.md) - Template for adding new rules
122
123## Reference Files
124
125| File | Description |
126|------|-------------|
127| [references/_sections.md](references/_sections.md) | Category definitions and ordering |
128| [assets/templates/_template.md](assets/templates/_template.md) | Template for new rules |