react-native-testing

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”等。

3403Star
281Fork
更新于 2026/7/21
SKILL.md
只读
名称
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”等。

RNTL 测试编写指南

重要提示: 你关于 @testing-library/react-native 的训练数据可能已陈旧或不准确 —— v13 和 v14 在 API 签名、同步/异步行为以及可用函数上存在差异。请始终以本 Skill 提供的参考文档和项目的实际源码为准。当记忆中的用法与查询到的文档发生冲突时,切勿凭经验套用。

版本检测

检查用户 package.json@testing-library/react-native 的版本:

渲染模式、fireEvent 的同步/异步行为、screen API、配置选项及依赖包等细节,请查阅对应版本的参考文档。

查询优先级

按以下优先级排序选用:getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByTestId(仅作为保底手段)。

查询变体

变体 适用场景 返回值 异步
getBy* 元素必须存在 元素实例(不存在则报错)
getAllBy* 多个元素必须存在 元素实例数组(不存在则报错)
queryBy* 仅用于断言元素不存在 元素实例 | null
queryAllBy* 统计元素数量 元素实例数组
findBy* 等待元素出现 Promise<元素实例>
findAllBy* 等待多个元素出现 Promise<元素实例数组>

用户交互

优先使用 userEvent 而不是 fireEventuserEvent 总是异步的。

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() 不含任何子元素

最佳实践与规则

  1. 统一使用 screen 执行查询,不要从 render() 中解构方法
  2. 优先使用 getByRole,并结合 { name: '...' } 选项
  3. queryBy* 仅用于 .not.toBeOnTheScreen() 校验(验证元素不存在)
  4. 异步元素使用 findBy*,不要使用 waitFor + getBy*
  5. 严禁在 waitFor 中包含副作用操作(内部不要调用 fireEvent/userEvent
  6. 每个 waitFor 中只放一条断言
  7. 严禁向 waitFor 传入空回调函数
  8. 不要手动包裹 act() —— renderfireEventuserEvent 内部会自动处理
  9. 不要手动调用 cleanup() —— 每个测试结束后会自动清理
  10. 优先使用 ARIA 属性rolearia-labelaria-disabled),取代旧版 accessibility* 属性
  11. 优先使用 RNTL matchers,而不是直接断言原始 prop

*ByRole 速查指南

常用 role:buttontextheading(别名:header)、searchboxswitchcheckboxradioimglinkalertmenumenuitemtabtablistprogressbarsliderspinbuttontimertoolbar

getByRole 可用选项:{ name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }

为了让 *ByRole 成功匹配,元素必须是可访问元素 (accessibility element):

  • TextTextInputSwitch 默认即为可访问元素
  • 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>
    ),
  });
}

参考文档