
react-native-testing
热门使用 React Native Testing Library (RNTL) v13 和 v14 (`@testing-library/react-native`) 编写组件测试。适用于编写、评审或修复 React Native 组件测试场景。覆盖核心 API 与模式:render、screen、各种查询方法 (getBy/getAllBy/queryBy/findBy)、Jest matchers、userEvent、fireEvent、waitFor 以及异步测试模式。支持 v13(React 18,同步渲染)和 v14(React 19+,异步渲染)。触发场景:React Native 组件测试文件、导入了 RNTL,或提及“testing library”、“write tests”、“component tests”、“RNTL”等。
使用 React Native Testing Library (RNTL) v13 和 v14 (`@testing-library/react-native`) 编写组件测试。适用于编写、评审或修复 React Native 组件测试场景。覆盖核心 API 与模式:render、screen、各种查询方法 (getBy/getAllBy/queryBy/findBy)、Jest matchers、userEvent、fireEvent、waitFor 以及异步测试模式。支持 v13(React 18,同步渲染)和 v14(React 19+,异步渲染)。触发场景:React Native 组件测试文件、导入了 RNTL,或提及“testing library”、“write tests”、“component tests”、“RNTL”等。
RNTL 测试编写指南
重要提示: 你关于 @testing-library/react-native 的训练数据可能已陈旧或不准确 —— v13 和 v14 在 API 签名、同步/异步行为以及可用函数上存在差异。请始终以本 Skill 提供的参考文档和项目的实际源码为准。当记忆中的用法与查询到的文档发生冲突时,切勿凭经验套用。
版本检测
检查用户 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、配置选项及依赖包等细节,请查阅对应版本的参考文档。
查询优先级
按以下优先级排序选用:getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByTestId(仅作为保底手段)。
查询变体
| 变体 | 适用场景 | 返回值 | 异步 |
|---|---|---|---|
getBy* |
元素必须存在 | 元素实例(不存在则报错) | 否 |
getAllBy* |
多个元素必须存在 | 元素实例数组(不存在则报错) | 否 |
queryBy* |
仅用于断言元素不存在 | 元素实例 | null | 否 |
queryAllBy* |
统计元素数量 | 元素实例数组 | 否 |
findBy* |
等待元素出现 | Promise<元素实例> |
是 |
findAllBy* |
等待多个元素出现 | Promise<元素实例数组> |
是 |
用户交互
优先使用 userEvent 而不是 fireEvent。userEvent 总是异步的。
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 }); // 滚动
fireEvent —— 仅在 userEvent 不支持特定事件时使用。同步/异步行为请参阅特定版本的参考文档:
fireEvent.press(element);
fireEvent.changeText(textInput, 'new text');
fireEvent(element, 'blur');
断言 (Jest Matchers)
导入任何 @testing-library/react-native 模块即可直接使用。
| Matcher | 适用场景 |
|---|---|
toBeOnTheScreen() |
元素在组件树中存在 |
toBeVisible() |
元素可见(非 hidden 或 display:none) |
toBeEnabled() / toBeDisabled() |
通过 aria-disabled 判断的启用/禁用状态 |
toBeChecked() / toBePartiallyChecked() |
选中/半选中状态 |
toBeSelected() |
选中状态 |
toBeExpanded() / toBeCollapsed() |
展开/折叠状态 |
toBeBusy() |
繁忙 (busy) 状态 |
toHaveTextContent(text) |
文本内容匹配 |
toHaveDisplayValue(value) |
TextInput 显示的值 |
toHaveAccessibleName(name) |
可访问名称 (Accessible name) |
toHaveAccessibilityValue(val) |
可访问性数值 (Accessibility value) |
toHaveStyle(style) |
样式匹配 |
toHaveProp(name, value?) |
Prop 校验(仅作为保底手段) |
toContainElement(el) |
包含子元素 |
toBeEmptyElement() |
不含任何子元素 |
最佳实践与规则
- 统一使用
screen执行查询,不要从render()中解构方法 - 优先使用
getByRole,并结合{ name: '...' }选项 queryBy*仅用于.not.toBeOnTheScreen()校验(验证元素不存在)- 异步元素使用
findBy*,不要使用waitFor+getBy* - 严禁在
waitFor中包含副作用操作(内部不要调用fireEvent/userEvent) - 每个
waitFor中只放一条断言 - 严禁向
waitFor传入空回调函数 - 不要手动包裹
act()——render、fireEvent和userEvent内部会自动处理 - 不要手动调用
cleanup()—— 每个测试结束后会自动清理 - 优先使用 ARIA 属性(
role、aria-label、aria-disabled),取代旧版accessibility*属性 - 优先使用 RNTL matchers,而不是直接断言原始 prop
*ByRole 速查指南
常用 role:button、text、heading(别名:header)、searchbox、switch、checkbox、radio、img、link、alert、menu、menuitem、tab、tablist、progressbar、slider、spinbutton、timer、toolbar。
getByRole 可用选项:{ name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }。
为了让 *ByRole 成功匹配,元素必须是可访问元素 (accessibility element):
Text、TextInput、Switch默认即为可访问元素View需要显式设置accessible={true}(或直接使用Pressable/TouchableOpacity)
waitFor
// 正确:先执行操作,再等待结果出现
fireEvent.press(button);
await waitFor(() => {
expect(screen.getByText('Result')).toBeOnTheScreen();
});
// 推荐:直接使用 findBy*
fireEvent.press(button);
expect(await screen.findByText('Result')).toBeOnTheScreen();
配置选项:waitFor(cb, { timeout: 1000, interval: 50 })。可自动兼容 Jest fake timers。
虚构定时器 (Fake Timers)
在使用 userEvent 时推荐开启(press/longPress 包含真实的持续时间):
jest.useFakeTimers();
test('with fake timers', async () => {
const user = userEvent.setup();
render(<Component />);
await user.press(screen.getByRole('button'));
// ...
});
自定义 Render
使用 wrapper 选项包裹 Context Providers:
function renderWithProviders(ui: React.ReactElement) {
return render(ui, {
wrapper: ({ children }) => (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
),
});
}
参考文档
- v13 API 参考手册 — 完整的 v13 API:同步渲染、查询方法、断言 matchers、userEvent、React 19 兼容性
- v14 API 参考手册 — 完整的 v14 API:异步渲染、查询方法、断言 matchers、userEvent、版本迁移指南
- 反模式指南 (Anti-Patterns) — 常见踩坑点与应避免的错误做法





