Skip to content

Commit 62820cd

Browse files
committed
doc: llm guidelines
1 parent 3b4622d commit 62820cd

File tree

2 files changed

+162
-12
lines changed

2 files changed

+162
-12
lines changed

website/docs/14.x/docs/guides/common-mistakes.mdx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -625,15 +625,13 @@ This works fine, but it's more conventional to name it something more descriptiv
625625

626626
The key principles to remember:
627627

628-
1. Use the right query for your use case (prefer `getByRole`)
629-
2. Use `findBy*` for async elements instead of `waitFor` + `getBy*`
630-
3. Use `queryBy*` only for checking non-existence
631-
4. Avoid side-effects in `waitFor`
632-
5. Use ARIA attributes (`role`, `aria-label`) instead of `accessibility*` props
633-
6. Use the right assertions (RNTL matchers)
634-
7. Prefer `screen` over destructuring from `render`
635-
8. Use `userEvent` for realistic user interactions
636-
9. Remember that `render`, `fireEvent`, and `renderHook` are async in v14
637-
10. Don't use `container.queryAll` directly - use proper queries
638-
639-
By following these guidelines, your tests will be more maintainable, accessible, and reliable.
628+
1. **Use the right query** - Prefer `getByRole` as your first choice, use `findBy*` for async elements, and `queryBy*` only for checking non-existence
629+
2. **Use proper assertions** - Use RNTL's built-in matchers (`toBeOnTheScreen()`, `toBeDisabled()`, etc.) instead of asserting on props directly
630+
3. **Handle async operations correctly** - Always `await` `render()`, `fireEvent`, `renderHook`, and `userEvent` methods in v14
631+
4. **Use `waitFor` correctly** - Avoid side-effects in callbacks, use `findBy*` instead when possible, and keep callbacks focused
632+
5. **Follow accessibility best practices** - Prefer ARIA attributes (`role`, `aria-label`) over `accessibility*` props
633+
6. **Organize code well** - Use `screen` over destructuring, prefer `userEvent` over `fireEvent`, and don't use `cleanup()`
634+
635+
For detailed guidelines and examples, see the [LLM Guidelines](llm-guidelines) document.
636+
637+
By following these principles, your tests will be more maintainable, accessible, and reliable.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# LLM Guidelines for React Native Testing Library
2+
3+
Actionable guidelines for writing tests with React Native Testing Library (RNTL) v14.
4+
5+
## Query Selection
6+
7+
- **Prefer `getByRole`** as first choice for querying elements
8+
- **Query priority**: `getByRole``getByLabelText``getByPlaceholderText``getByText``getByDisplayValue``getByTestId` (last resort)
9+
- **Use `findBy*`** for elements that appear asynchronously (after API calls, timeouts, state updates)
10+
- **Use `queryBy*` ONLY** for checking non-existence (with `.not.toBeOnTheScreen()`)
11+
- **Never use `getBy*`** for non-existence checks
12+
- **Avoid `container.queryAll()`** - use `screen` queries instead
13+
- **Query by visible text**, not `testID` when text is available
14+
15+
## Assertions
16+
17+
- **Use RNTL matchers**: `toBeOnTheScreen()`, `toBeDisabled()`, `toHaveTextContent()`, `toHaveAccessibleName()`
18+
- **Don't assert on props directly** - use semantic matchers instead
19+
- **Combine queries with matchers**: `expect(screen.getByText('Hello')).toBeOnTheScreen()`
20+
- **No redundant null checks** - `getBy*` already throws if not found
21+
22+
## Async/Await (v14)
23+
24+
- **Always `await`**: `render()`, `fireEvent.*`, `renderHook()`, `userEvent.*`
25+
- **Make test functions `async`**: `test('name', async () => { ... })`
26+
- **Don't wrap in `act()`** - `render` and `fireEvent` handle it internally
27+
28+
## waitFor Usage
29+
30+
- **Use `findBy*`** instead of `waitFor` + `getBy*` when waiting for elements
31+
- **Never perform side-effects** (like `fireEvent.press()`) inside `waitFor` callbacks
32+
- **One assertion per `waitFor`** callback
33+
- **Never pass empty callbacks** - always include a meaningful assertion
34+
- **Place side-effects before `waitFor`** - perform actions, then wait for result
35+
36+
## Accessibility
37+
38+
- **Prefer ARIA attributes** over `accessibility*` props:
39+
- `role` instead of `accessibilityRole`
40+
- `aria-label` instead of `accessibilityLabel`
41+
- `aria-disabled` instead of `accessibilityState={{ disabled: true }}`
42+
- `aria-checked` instead of `accessibilityState={{ checked: true }}`
43+
- `aria-selected` instead of `accessibilityState={{ selected: true }}`
44+
- `aria-expanded` instead of `accessibilityState={{ expanded: true }}`
45+
- `aria-busy` instead of `accessibilityState={{ busy: true }}`
46+
- **Only add necessary attributes** - don't add unnecessary accessibility props
47+
- **Use `role` prop** on interactive elements for better querying
48+
49+
## Code Organization
50+
51+
- **Use `screen`** instead of destructuring from `render()`: `screen.getByText()` not `const { getByText } = render()`
52+
- **Prefer `userEvent`** over `fireEvent` for realistic interactions
53+
- **Don't use `cleanup()`** - handled automatically
54+
- **Name wrappers descriptively**: `ThemeProvider` not `Wrapper`
55+
- **Install ESLint plugin**: `eslint-plugin-testing-library`
56+
57+
## Quick Checklist
58+
59+
- ✅ Using `getByRole` as first choice?
60+
- ✅ Using `await` for all async operations?
61+
- ✅ Using `findBy*` for async elements (not `waitFor` + `getBy*`)?
62+
- ✅ Using `queryBy*` only for non-existence?
63+
- ✅ Using RNTL matchers (`toBeOnTheScreen()`, `toBeDisabled()`, etc.)?
64+
- ✅ Using ARIA attributes (`role`, `aria-label`) not `accessibility*` props?
65+
- ✅ Using `screen` not destructuring from `render()`?
66+
- ✅ Avoiding side-effects in `waitFor`?
67+
- ✅ Using `userEvent` when appropriate?
68+
69+
## Example: Good Pattern
70+
71+
```tsx
72+
import { render, screen } from '@testing-library/react-native';
73+
import userEvent from '@testing-library/react-native';
74+
import { Pressable, Text, TextInput, View } from 'react-native';
75+
76+
test('user can submit form', async () => {
77+
const user = userEvent.setup();
78+
79+
const Component = () => {
80+
const [name, setName] = React.useState('');
81+
const [submitted, setSubmitted] = React.useState(false);
82+
83+
return (
84+
<View>
85+
<TextInput
86+
role="textbox"
87+
aria-label="Name"
88+
value={name}
89+
onChangeText={setName}
90+
/>
91+
<Pressable
92+
role="button"
93+
aria-label="Submit"
94+
onPress={() => setSubmitted(true)}
95+
>
96+
<Text>Submit</Text>
97+
</Pressable>
98+
{submitted && <Text role="alert">Form submitted!</Text>}
99+
</View>
100+
);
101+
};
102+
103+
await render(<Component />);
104+
105+
// ✅ getByRole as first choice
106+
const input = screen.getByRole('textbox', { name: 'Name' });
107+
const button = screen.getByRole('button', { name: 'Submit' });
108+
109+
// ✅ userEvent for realistic interactions
110+
await user.type(input, 'John Doe');
111+
await user.press(button);
112+
113+
// ✅ findBy* for async elements
114+
const successMessage = await screen.findByRole('alert');
115+
116+
// ✅ RNTL matchers
117+
expect(successMessage).toBeOnTheScreen();
118+
expect(successMessage).toHaveTextContent('Form submitted!');
119+
});
120+
```
121+
122+
## Example: Anti-Patterns
123+
124+
```tsx
125+
// ❌ Missing await
126+
test('bad', () => {
127+
render(<Component />);
128+
fireEvent.press(screen.getByText('Submit'));
129+
});
130+
131+
// ❌ getBy* for non-existence
132+
expect(screen.getByText('Error')).not.toBeOnTheScreen();
133+
134+
// ❌ waitFor + getBy* instead of findBy*
135+
await waitFor(() => {
136+
expect(screen.getByText('Loaded')).toBeOnTheScreen();
137+
});
138+
139+
// ❌ Side-effect in waitFor
140+
await waitFor(async () => {
141+
await fireEvent.press(button);
142+
expect(screen.getByText('Result')).toBeOnTheScreen();
143+
});
144+
145+
// ❌ accessibility* props instead of ARIA
146+
<Pressable accessibilityRole="button" accessibilityLabel="Submit" />
147+
148+
// ❌ Destructuring from render
149+
const { getByText } = await render(<Component />);
150+
```
151+
152+
By following these guidelines, your tests will be more maintainable, accessible, and reliable.

0 commit comments

Comments
 (0)