您的 React Native 测试套件是绿色的,但用户仍然报告说在真实设备上点击“继续”时什么也没有发生。组件测试找到了按钮,调用了其处理程序,并在模拟 JavaScript 环境中看到预期屏幕。它从未验证原生权限提示、键盘行为、动画、平台 API 或实际导航堆栈。
这就是团队获得错误信心的地方。 React Native 测试库 React Native 测试库对于测试组件渲染和响应用户交互的方式非常出色,但它并不是设备测试或性能测量的替代品。可靠的策略是使用每个层次来暴露它可以暴露的失败。
目录
用户中心测试改变了什么
测试失败的常见原因始于测试写之前。开发人员检查组件的属性,进入其状态,或者比较一个大型快照,因为这些细节很容易断言。测试通过,然后重构改变了组件结构而没有改变体验,测试套件就破裂了。更糟糕的是,测试可以继续通过,而用户可见的行为是错误的,因为断言从未描述过用户需要看到的内容。
React Native Testing Library采取了相反的方法。您渲染组件,通过可见的控件与其交互,然后断言用户可以观察到的结果。这种方法与 React Native Testing Library的用户中心示例以及React Native的测试指导原则相符,测试应该短小,专注于一个问题,分离视图关注点与业务逻辑和状态,优先使用可见输出或辅助性输出而不是内部实现细节。

测试用户依赖的行为
假设登录表单在请求运行时禁用其提交按钮。脆弱的测试可能会检查 disabled 在某个组件实例上或断言状态变量发生了变化。一个更强的测试会按下可访问的“登录”控件,等待加载指示器,并检查错误消息或目的屏幕是否出现。
第二个测试并不关心表单是否使用本地状态、减少器、自定义钩子或不同的按钮实现。它关心的是应用程序传达的结果是否正确。
实践规则: 如果用户无法观察到它,是否该行为属于组件测试的范畴就值得怀疑。
基于可访问性标签和角色进行的查询也会迫使产品更好地进行code。暴露了有意义标签的屏幕更容易使用辅助技术,也更容易在测试中进行。这种联系在评估更广泛的 应用程序用户体验时尤其重要,因为可测试性和可用性往往会一起提高。
了解什么是通过测试不能证明的
用户中心的组件测试可以证明JavaScript渲染了预期的 branch 并对模拟按压做出了反应。它不能证明生物识别提示是否正确打开,原生摄像头是否返回可用的结果,还是支付流程是否能在平台特定的生命周期行为下存活下来。
这种界限并不是库的弱点。它是测试层保持诚实的原因。使用 RNTL 进行组件行为,然后将设备测试保留用于流程,其中原生集成、真实导航、权限、身份验证、支付或核心应用程序功能可以改变结果。
安装和配置确实有效
现代设置始于以下scoped包:
@testing-library/react-native
较旧的 react-native-testing-library npm包名仍然存在于历史文档中,但当前项目应使用Testing Library家族维护的scoped包。该项目的 GitHub仓库 描述了它作为React Native的实用工具,鼓励良好的测试实践,并且其发布历史表明该库一直在随着React Native的变化而变化,而不是保持静态。
使用现有的Jest环境安装该库。Expo项目通常使用Expo Jest预设,而裸露的React Native项目可能使用React Native预设:
npm install --save-dev @testing-library/react-native jest
对于Expo,添加项目中已有的预设:
{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "jest-expo"
}
}
对于裸露的React Native应用程序,使用相应的React Native Jest配置。保持预设与框架版本的对齐。许多看起来像RNTL问题的失败都是由于匹配不上的React渲染器、Babel转换或Jest预设而导致的。
保持设置文件明确
将共享环境配置放在设置文件中,而不是在每个测试中重复模拟:
// jest.setup.js
import '@testing-library/react-native/extend-expect';
然后从Jest中引用它:
{
"jest": {
"preset": "jest-expo",
"setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
}
}
如果您的应用程序使用 React Navigation、Reanimated、手势处理、安全区域上下文或存储模块,请仅配置测试环境需要的模拟。全局模拟会改变应用程序行为,使每个测试更容易通过,但使测试套件更不可信。
TypeScript 需要同样的关注。确保 Jest 转换 .ts and .tsx 127 个发布
, v14.0.1 在 2026-06-23 标记, v12.9.0, 在 2024-11-27 发布,添加了对 React Native 0.77 和 Expo 52 的官方支持。. v14 alpha 行在 2025 年 3 月从已弃用的 React Test Renderer 切换到 Universal Test Renderer,并为 React 19-only 支持做好准备。这些细节出现在 RNTL 发布历史,所以不要从过时的教程中复制一个渲染器依赖项而不检查你的应用程序的版本。
为了实用的 Jest 基础,比较你的配置与这个 Jest 单元测试指南然后在添加导航和本机模拟之前运行一个小组件测试。

核心 API 查询和断言解释
当 RNTL 测试的查询与用户可以看到、找到和操作的内容匹配时,测试才会变得可信赖。API 是小的,但选择暴露实现细节的选择器可以使一个通过的测试变得误导性。
从 render:
const screen = render(<LoginForm />);
选择最相关的用户查询。优先使用可访问性导向的查询,当组件暴露它们时,使用可见文本,当文本是行为时,保留 testID 用于没有意义的用户界面选择器或需要稳定的集成钩子时的

根据时间和意图选择查询
每个查询家族都有一个独特的目的:
- 同步存在: 使用
getByRole,getByText或另一个getBy当元素应该已经存在时使用该查询。测试立即失败,如果它不这样做。 - 异步出现: 使用
findByRole或findByText当渲染或交互导致异步更新时 - 缺失检查: 使用
queryByText或queryByTestId当元素可能不存在时,期望返回 null 而不是抛出异常。 - 回退选择器: 使用
getByTestId故意使用。它为复杂控件提供了一个可靠的钩子,但不应替代整个应用程序的可访问标签。
对于焦点事件测试, fireEvent.press 是直接的:
fireEvent.press(screen.getByRole('button', { name: 'Save' }));
使用 userEvent 当安装的版本支持更现实的交互序列时。无论哪种方式,断言结果的 UI:
expect(await screen.findByText('Saved')).toBeTruthy();
一个处理程序断言适用于一个公共契约是事件回调的组件。它是弱的,因为它是唯一的证据,用户旅程有效。
优先使用具体断言而不是快照
好的断言描述屏幕:
expect(screen.getByText('Account created')).toBeTruthy();
expect(screen.getByRole('button', { name: 'Continue' })).toBeEnabled();
它们还可以验证可访问性状态、选择和可见的验证反馈。避免检查内部连接:
expect(screen.getByTestId('submit-button').props.onPress).toBeDefined();
That assertion proves a prop exists, not that the feature works. Small, intentional snapshots can catch structural changes, while large navigation or screen snapshots often create noisy reviews and make unexplained updates easy to approve.
该断言证明了一个属性存在,而不是证明了该功能有效。小心意图的快照可以捕捉结构变化,而大型导航或屏幕快照通常会创建噪音的评论并使未经说明的更新容易批准。 testing-library npm 组织。其活跃包包括 @testing-library/react-native测试库包属于更广泛的 __CAPGO_KEEP_0____CAPGO_KEEP_0__ , with version,版本
13.3.3 published in 2026 13.3.3发布于2026年, according to the project’s
,根据项目的信息
实用模式:组件、钩子和导航
一个有用的测试套件应该遵循应用程序的形状。展示性组件需要直接行为测试,钩子需要受控的输入和输出,导航需要一个足够现实的提供商,而原生模块需要模拟,这些模拟应该与真实设备验证有明显的区别。

展示性组件
保持组件测试与其公共契约接近:
const onSelect = jest.fn();
render(
<PlanCard
title="Team"
description="Shared workspace"
onSelect={onSelect}
/>
);
fireEvent.press(screen.getByRole('button', { name: 'Choose Team' }));
expect(onSelect).toHaveBeenCalled();
确保标签的准确性与 UI 一致。重要的是测试应该找到控制器的方式,用户或辅助技术服务一样,并确认可见或回调结果的重要部分。不要默认地模拟每个子组件。只有当它们遮蔽测试行为时,才模拟昂贵或无关的边界。
对于一个自定义钩子,使用 renderHook 当安装的 RNTL 版本提供它时:
const { result } = renderHook(() => useSearch());
await act(async () => {
await result.current.submit('query');
});
expect(result.current.status).toBe('success');
钩子测试应该控制网络或仓库边界,而不是重现整个应用程序。测试屏幕单独,以便您知道钩子的状态是否有用。
导航和异步数据
对于导航行为,渲染一个屏幕在一个真实的 NavigationContainer 和一个小的测试导航器通常比模拟每个导航方法更有价值。按一个可见的控制器,等待目的地内容,断言新屏幕的输出。一个直接 useNavigation 模拟仍然适合于一个小按钮,其唯一责任是分发一个类型化的路由,但它不会验证路由注册、参数或嵌套导航器行为。
异步数据 deserves 同样的 discipline。模拟 API 或仓库响应,渲染屏幕,断言加载状态,解决请求,然后断言成功或错误输出。使用 findBy 预期的元素的查询,并且使拒绝的请求显式,否则一个测试可以通过,因为组件从未达到意向的 branch。
原生模块模拟
AsyncStorage、权限、摄像头、生物识别和平台 API 的模拟对于可预测的 JavaScript 测试有用。它们不是原生特性的证据。保持模拟行为与模块协议接近,测试之间重置调用,并包括失败响应而不是仅仅模型化快乐路径。
| 场景 | 最佳与 RNTL | 需要 E2E 验证 |
|---|---|---|
| 表单验证和可见错误 | 是 | 通常不需要 |
| 通常不需要的项是: | 是 | 对于关键生产流程 |
| 注册屏幕之间的导航 | 是,带有测试导航器 | 是,当手势、深链接或平台行为很重要时 |
| AsyncStorage状态决策 | 是,带有控制的模拟 | 是,当启动和持久性与原生生命周期相互作用时 |
| 相机、生物识别、权限或平台API | JS fallback和分支逻辑 | 是,在真实或代表性设备上 |
| 布局、渲染性能和原生code | No | 使用设备或专门工具 |
测试时,边界是实际的:模拟依赖项来测试JavaScript决策,然后在设备上运行测试以确认依赖项的真实行为。
调试易碎测试CI和性能检查
易碎测试通常指出未控制的时间、共享状态或UI竞争的断言。识别存在的条件之前不要增加超时。更长的超时可能会隐藏调度问题并使套件变慢。
使用 findBy 对于预期在更新后出现的元素。使用 waitFor 对于状态条件或模拟调用。如果启用了虚拟时钟,务必要在交互所需的点上推进它们,并在之后恢复真实时钟。一个 act 警告意味着React观察到了超出预期的交互边界的更新。修复缺失的 await用户交互或定时器刷新,而不是抑制警告。
使CI失败可复现
可靠的CI工作从锁文件安装,运行本地使用的相同Jest命令,并隔离模拟状态。清除测试之间的模拟调用,重置模块,当模块级别的状态影响行为时,重置模块,并移除对执行顺序的依赖。Jest缓存加速反馈,但依赖项或配置更改需要适当的缓存失效。
CI-only failures需要在组件重写之前进行环境比较。检查Node、包管理器、Jest工作器设置、计时器配置和环境变量。尽可能地在本地重现相同的命令,然后将失败的测试缩小到最小的交互以暴露差异。
可观察性填补了不同的缺口。类似于 React Native的Sentry 供应生产错误上下文,模拟组件测试无法复制的错误,包括与真实设备和原生集成相关的失败。
安全敏感的旅程需要第二个边界。使用组件测试进行验证和状态转换,然后添加设备级别的覆盖,用于手动和原生行为。对于一次性code流程,请参阅有关如何 测试SMS验证流程 的指导
当该集成形成旅程的一部分时。
将性能视为测量,而不是断言 性能测试文档 性能测试文档
避免使用固定的断言,如“渲染必须在选择的阈值以下完成”。忙碌的CI工人可能会触发假失败,而松散的阈值可能会错过真正的回归。使用重复测量来捕获回归信号,然后在比较显示有意义差异时检查组件和设备配置。
测量规则: 功能测试回答行为是否正确。性能工具回答是否测量场景发生了变化。保持这些问题分开。
将所有内容整合起来并继续前进
React Native Testing Library属于快速层的广泛测试策略中。它应该涵盖组件行为、可见状态变化、可访问性结果、验证、模拟数据状态和JavaScript组件之间的集成。将这些测试聚焦在用户可以观察到的内容上,并且使失败指向特定的行为而不是大型渲染树。
较小的设备层应该保护模拟数据可能存在的流程。React Native的 测试概述 指出RNTL不提供完整的React Native运行时,并且无法测试原生特性。同样的指导建议将组件测试与E2E工具如Detox一起使用,用于关键流程,包括身份验证、支付和核心应用功能。
实用的迁移路径
您不需要一次性重写现有的套件。
- 保留有价值的业务测试。 将纯粹的状态和域逻辑移到聚焦的单元测试中,提供清晰的反馈。
- 替换实现断言。 将属性和内部状态检查转换为可见输出、可访问性状态和交互结果。
- 压缩快照。 只保留审阅者可以理解和维护的快照。
- 添加本机边界覆盖。 对于每个重要的模拟模块,确定仍需要验证的设备行为。
- 保护关键旅程。 添加E2E覆盖度,包括身份验证、支付、核心导航、权限和其他流程,native行为可能会改变结果。
- 单独测量敏感屏幕。 使用重复的性能比较来比较列表、feed和昂贵的渲染路径,而不是根据Jest持续时间猜测。
为了释放信心,将套件连接到 CI/CD集成测试 并且在交付之前需要适当的测试层。如果一个JavaScript或资产修复必须在验证后到达用户,Capgo可以将已签名的Web包传递给CapacitorJS和Electron应用的目标通道,具有滚动控制和回滚保护。该交付工作流程不会替代React Native设备测试,但它说明了同样的原则:在它运行的层面上验证行为。
正确的问题不是React Native Testing Library是否可以测试您的整个应用。它不能,并且官方的边界是有用的。正确的问题是每个重要风险是否有一个在能够暴露它的环境中运行的测试。
Capgo将验证的JavaScript和资产更改连接到CapacitorJS和Electron团队的控制传递,具有目标通道、滚动可见性和回滚保护。访问 Capgo 查看它如何与您的组件、E2E和CI测试工作流程一起工作。