react-native-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRNTL Test Writing Guide
RNTL测试编写指南
IMPORTANT: Your training data about may be outdated or incorrect — API signatures, sync/async behavior, and available functions differ between v13 and v14. Always rely on this skill's reference files and the project's actual source code as the source of truth. Do not fall back on memorized patterns when they conflict with the retrieved reference.
@testing-library/react-native重要提示: 你所掌握的相关知识可能已过时或不准确——v13和v14版本在API签名、同步/异步行为以及可用函数上存在差异。请始终以本技能的参考文件和项目实际源代码为权威依据。当记忆中的模式与检索到的参考内容冲突时,请勿沿用记忆内容。
@testing-library/react-nativeVersion Detection
版本检测
Check version in the user's :
@testing-library/react-nativepackage.json- v14.x → load references/api-reference-v14.md (React 19+, async APIs, )
test-renderer - v13.x → load references/api-reference-v13.md (React 18+, sync APIs, )
react-test-renderer
Use the version-specific reference for render patterns, fireEvent sync/async behavior, screen API, configuration, and dependencies.
检查用户中的版本:
package.json@testing-library/react-native- v14.x → 加载references/api-reference-v14.md(React 19+,异步API,)
test-renderer - v13.x → 加载references/api-reference-v13.md(React 18+,同步API,)
react-test-renderer
请使用对应版本的参考文档来处理渲染模式、fireEvent的同步/异步行为、screen API、配置以及依赖项相关内容。
Query Priority
查询优先级
Use in this order: > > > > > (last resort).
getByRolegetByLabelTextgetByPlaceholderTextgetByTextgetByDisplayValuegetByTestId请按照以下优先级使用查询方法: > > > > > (最后选择)。
getByRolegetByLabelTextgetByPlaceholderTextgetByTextgetByDisplayValuegetByTestIdQuery Variants
查询变体
| Variant | Use case | Returns | Async |
|---|---|---|---|
| Element must exist | element instance (throws) | No |
| Multiple must exist | element instance[] (throws) | No |
| Check non-existence ONLY | element instance | null | No |
| Count elements | element instance[] | No |
| Wait for element | | Yes |
| Wait for multiple | | Yes |
| 变体 | 使用场景 | 返回值 | 是否异步 |
|---|---|---|---|
| 元素必须存在 | 元素实例(不存在则抛出错误) | 否 |
| 多个元素必须存在 | 元素实例数组(不存在则抛出错误) | 否 |
| 仅用于检查元素不存在 | 元素实例 | null | 否 |
| 统计元素数量 | 元素实例数组 | 否 |
| 等待元素出现 | | 是 |
| 等待多个元素出现 | | 是 |
Interactions
交互操作
Prefer over . userEvent is always async.
userEventfireEventtsx
const user = userEvent.setup();
await user.press(element); // full press sequence
await user.longPress(element, { duration: 800 }); // long press
await user.type(textInput, 'Hello'); // char-by-char typing
await user.clear(textInput); // clear TextInput
await user.paste(textInput, 'pasted text'); // paste into TextInput
await user.scrollTo(scrollView, { y: 100 }); // scrollfireEventuserEventtsx
fireEvent.press(element);
fireEvent.changeText(textInput, 'new text');
fireEvent(element, 'blur');优先使用而非。userEvent始终是异步的。
userEventfireEventtsx
const user = userEvent.setup();
await user.press(element); // 完整的点击序列
await user.longPress(element, { duration: 800 }); // 长按
await user.type(textInput, 'Hello'); // 逐字符输入
await user.clear(textInput); // 清空TextInput
await user.paste(textInput, 'pasted text'); // 粘贴到TextInput
await user.scrollTo(scrollView, { y: 100 }); // 滚动fireEventuserEventtsx
fireEvent.press(element);
fireEvent.changeText(textInput, 'new text');
fireEvent(element, 'blur');Assertions (Jest Matchers)
断言(Jest匹配器)
Available automatically with any import.
@testing-library/react-native| Matcher | Use for |
|---|---|
| Element exists in tree |
| Element visible (not hidden/display:none) |
| Disabled state via |
| Checked state |
| Selected state |
| Expanded state |
| Busy state |
| Text content match |
| TextInput display value |
| Accessible name |
| Accessibility value |
| Style match |
| Prop check (last resort) |
| Contains child element |
| No children |
导入后即可自动使用这些匹配器。
@testing-library/react-native| 匹配器 | 适用场景 |
|---|---|
| 元素存在于组件树中 |
| 元素可见(未被隐藏/未设置display:none) |
| 通过 |
| 选中状态 |
| 选中状态(如选项卡) |
| 展开/折叠状态 |
| 忙碌状态 |
| 文本内容匹配 |
| TextInput显示值 |
| 可访问名称 |
| 可访问值 |
| 样式匹配 |
| 属性检查(最后选择的方案) |
| 包含子元素 |
| 无子元素 |
Rules
规则
- Use for queries, not destructuring from
screenrender() - Use first with
getByRoleoption{ name: '...' } - Use ONLY for
queryBy*checks.not.toBeOnTheScreen() - Use for async elements, NOT
findBy*+waitForgetBy* - Never put side-effects in (no
waitFor/fireEventinside)userEvent - One assertion per
waitFor - Never pass empty callbacks to
waitFor - Don't wrap in -
act(),render,fireEventhandle ituserEvent - Don't call - automatic after each test
cleanup() - Prefer ARIA props (,
role,aria-label) over legacyaria-disabledpropsaccessibility* - Use RNTL matchers over raw prop assertions
- **使用**进行查询,而非从
screen中解构获取render() - 优先使用,并配合
getByRole选项{ name: '...' } - 仅在检查元素不存在时使用(配合
queryBy*).not.toBeOnTheScreen() - **使用**处理异步元素,而非
findBy*+waitForgetBy* - 不要在中放入副作用操作(内部不要调用
waitFor/fireEvent)userEvent - 每个中仅包含一个断言
waitFor - 不要向传递空回调函数
waitFor - 不要用包裹 -
act()、render、fireEvent会自动处理userEvent - 不要调用- 每个测试后会自动执行
cleanup() - 优先使用ARIA属性(、
role、aria-label)而非旧版aria-disabled属性accessibility* - 使用RNTL匹配器而非直接断言属性
*ByRole
Quick Reference
*ByRole*ByRole
快速参考
*ByRoleCommon roles: , , (alias: ), , , , , , , , , , , , , , , , .
buttontextheadingheadersearchboxswitchcheckboxradioimglinkalertmenumenuitemtabtablistprogressbarsliderspinbuttontimertoolbargetByRole{ name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }For to match, the element must be an accessibility element:
*ByRole- ,
Text,TextInputare by defaultSwitch - needs
View(or useaccessible={true}/Pressable)TouchableOpacity
常见角色:、、(别名:)、、、、、、、、、、、、、、、、。
buttontextheadingheadersearchboxswitchcheckboxradioimglinkalertmenumenuitemtabtablistprogressbarsliderspinbuttontimertoolbargetByRole{ name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }要让匹配到元素,该元素必须是可访问元素:
*ByRole- 、
Text、TextInput默认是可访问元素Switch - 需要设置
View(或使用accessible={true}/Pressable)TouchableOpacity
waitFor
waitFor
tsx
// Correct: action first, then wait for result
fireEvent.press(button);
await waitFor(() => {
expect(screen.getByText('Result')).toBeOnTheScreen();
});
// Better: use findBy* instead
fireEvent.press(button);
expect(await screen.findByText('Result')).toBeOnTheScreen();Options: . Works with Jest fake timers automatically.
waitFor(cb, { timeout: 1000, interval: 50 })tsx
// 正确做法:先执行操作,再等待结果
fireEvent.press(button);
await waitFor(() => {
expect(screen.getByText('Result')).toBeOnTheScreen();
});
// 更优做法:使用findBy*替代
fireEvent.press(button);
expect(await screen.findByText('Result')).toBeOnTheScreen();选项:。可自动配合Jest假计时器使用。
waitFor(cb, { timeout: 1000, interval: 50 })Fake Timers
假计时器
Recommended with (press/longPress involve real durations):
userEventtsx
jest.useFakeTimers();
test('with fake timers', async () => {
const user = userEvent.setup();
render(<Component />);
await user.press(screen.getByRole('button'));
// ...
});推荐与配合使用(点击/长按涉及实际时长):
userEventtsx
jest.useFakeTimers();
test('with fake timers', async () => {
const user = userEvent.setup();
render(<Component />);
await user.press(screen.getByRole('button'));
// ...
});Custom Render
自定义渲染
Wrap providers using option:
wrappertsx
function renderWithProviders(ui: React.ReactElement) {
return render(ui, {
wrapper: ({ children }) => (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
),
});
}使用选项包裹提供者组件:
wrappertsx
function renderWithProviders(ui: React.ReactElement) {
return render(ui, {
wrapper: ({ children }) => (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
),
});
}References
参考文档
- v13 API Reference — Complete v13 API: sync render, queries, matchers, userEvent, React 19 compat
- v14 API Reference — Complete v14 API: async render, queries, matchers, userEvent, migration
- Anti-Patterns — Common mistakes to avoid
- v13 API参考 — 完整的v13 API:同步渲染、查询方法、匹配器、userEvent、React 19兼容性
- v14 API参考 — 完整的v14 API:异步渲染、查询方法、匹配器、userEvent、迁移指南
- 反模式 — 需要避免的常见错误