react-native-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

RNTL Test Writing Guide

RNTL测试编写指南

IMPORTANT: Your training data about
@testing-library/react-native
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签名、同步/异步行为以及可用函数上存在差异。请始终以本技能的参考文件和项目实际源代码为权威依据。当记忆中的模式与检索到的参考内容冲突时,请勿沿用记忆内容。

Version Detection

版本检测

Check
@testing-library/react-native
version in the user's
package.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:
getByRole
>
getByLabelText
>
getByPlaceholderText
>
getByText
>
getByDisplayValue
>
getByTestId
(last resort).
请按照以下优先级使用查询方法:
getByRole
>
getByLabelText
>
getByPlaceholderText
>
getByText
>
getByDisplayValue
>
getByTestId
(最后选择)。

Query Variants

查询变体

VariantUse caseReturnsAsync
getBy*
Element must existelement instance (throws)No
getAllBy*
Multiple must existelement instance[] (throws)No
queryBy*
Check non-existence ONLYelement instance | nullNo
queryAllBy*
Count elementselement instance[]No
findBy*
Wait for element
Promise<element instance>
Yes
findAllBy*
Wait for multiple
Promise<element instance[]>
Yes
变体使用场景返回值是否异步
getBy*
元素必须存在元素实例(不存在则抛出错误)
getAllBy*
多个元素必须存在元素实例数组(不存在则抛出错误)
queryBy*
仅用于检查元素不存在元素实例 | null
queryAllBy*
统计元素数量元素实例数组
findBy*
等待元素出现
Promise<元素实例>
findAllBy*
等待多个元素出现
Promise<元素实例数组>

Interactions

交互操作

Prefer
userEvent
over
fireEvent
. userEvent is always async.
tsx
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 }); // scroll
fireEvent
— use only when
userEvent
doesn't support the event. See version-specific reference for sync/async behavior:
tsx
fireEvent.press(element);
fireEvent.changeText(textInput, 'new text');
fireEvent(element, 'blur');
优先使用
userEvent
而非
fireEvent
。userEvent始终是异步的。
tsx
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
不支持该事件时使用。请查看对应版本的参考文档了解其同步/异步行为:
tsx
fireEvent.press(element);
fireEvent.changeText(textInput, 'new text');
fireEvent(element, 'blur');

Assertions (Jest Matchers)

断言(Jest匹配器)

Available automatically with any
@testing-library/react-native
import.
MatcherUse for
toBeOnTheScreen()
Element exists in tree
toBeVisible()
Element visible (not hidden/display:none)
toBeEnabled()
/
toBeDisabled()
Disabled state via
aria-disabled
toBeChecked()
/
toBePartiallyChecked()
Checked state
toBeSelected()
Selected state
toBeExpanded()
/
toBeCollapsed()
Expanded state
toBeBusy()
Busy state
toHaveTextContent(text)
Text content match
toHaveDisplayValue(value)
TextInput display value
toHaveAccessibleName(name)
Accessible name
toHaveAccessibilityValue(val)
Accessibility value
toHaveStyle(style)
Style match
toHaveProp(name, value?)
Prop check (last resort)
toContainElement(el)
Contains child element
toBeEmptyElement()
No children
导入
@testing-library/react-native
后即可自动使用这些匹配器。
匹配器适用场景
toBeOnTheScreen()
元素存在于组件树中
toBeVisible()
元素可见(未被隐藏/未设置display:none)
toBeEnabled()
/
toBeDisabled()
通过
aria-disabled
判断禁用状态
toBeChecked()
/
toBePartiallyChecked()
选中状态
toBeSelected()
选中状态(如选项卡)
toBeExpanded()
/
toBeCollapsed()
展开/折叠状态
toBeBusy()
忙碌状态
toHaveTextContent(text)
文本内容匹配
toHaveDisplayValue(value)
TextInput显示值
toHaveAccessibleName(name)
可访问名称
toHaveAccessibilityValue(val)
可访问值
toHaveStyle(style)
样式匹配
toHaveProp(name, value?)
属性检查(最后选择的方案)
toContainElement(el)
包含子元素
toBeEmptyElement()
无子元素

Rules

规则

  1. Use
    screen
    for queries, not destructuring from
    render()
  2. Use
    getByRole
    first
    with
    { name: '...' }
    option
  3. Use
    queryBy*
    ONLY
    for
    .not.toBeOnTheScreen()
    checks
  4. Use
    findBy*
    for async elements, NOT
    waitFor
    +
    getBy*
  5. Never put side-effects in
    waitFor
    (no
    fireEvent
    /
    userEvent
    inside)
  6. One assertion per
    waitFor
  7. Never pass empty callbacks to
    waitFor
  8. Don't wrap in
    act()
    -
    render
    ,
    fireEvent
    ,
    userEvent
    handle it
  9. Don't call
    cleanup()
    - automatic after each test
  10. Prefer ARIA props (
    role
    ,
    aria-label
    ,
    aria-disabled
    ) over legacy
    accessibility*
    props
  11. Use RNTL matchers over raw prop assertions
  1. **使用
    screen
    **进行查询,而非从
    render()
    中解构获取
  2. 优先使用
    getByRole
    ,并配合
    { name: '...' }
    选项
  3. 仅在检查元素不存在时使用
    queryBy*
    (配合
    .not.toBeOnTheScreen()
  4. **使用
    findBy*
    **处理异步元素,而非
    waitFor
    +
    getBy*
  5. 不要在
    waitFor
    中放入副作用操作
    (内部不要调用
    fireEvent
    /
    userEvent
  6. 每个
    waitFor
    中仅包含一个断言
  7. 不要向
    waitFor
    传递空回调函数
  8. 不要用
    act()
    包裹
    -
    render
    fireEvent
    userEvent
    会自动处理
  9. 不要调用
    cleanup()
    - 每个测试后会自动执行
  10. 优先使用ARIA属性
    role
    aria-label
    aria-disabled
    )而非旧版
    accessibility*
    属性
  11. 使用RNTL匹配器而非直接断言属性

*ByRole
Quick Reference

*ByRole
快速参考

Common roles:
button
,
text
,
heading
(alias:
header
),
searchbox
,
switch
,
checkbox
,
radio
,
img
,
link
,
alert
,
menu
,
menuitem
,
tab
,
tablist
,
progressbar
,
slider
,
spinbutton
,
timer
,
toolbar
.
getByRole
options:
{ name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }
.
For
*ByRole
to match, the element must be an accessibility element:
  • Text
    ,
    TextInput
    ,
    Switch
    are by default
  • View
    needs
    accessible={true}
    (or use
    Pressable
    /
    TouchableOpacity
    )
常见角色:
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
匹配到元素,该元素必须是可访问元素:
  • 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:
waitFor(cb, { timeout: 1000, interval: 50 })
. Works with Jest fake timers automatically.
tsx
// 正确做法:先执行操作,再等待结果
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

假计时器

Recommended with
userEvent
(press/longPress involve real durations):
tsx
jest.useFakeTimers();

test('with fake timers', async () => {
  const user = userEvent.setup();
  render(<Component />);
  await user.press(screen.getByRole('button'));
  // ...
});
推荐与
userEvent
配合使用(点击/长按涉及实际时长):
tsx
jest.useFakeTimers();

test('with fake timers', async () => {
  const user = userEvent.setup();
  render(<Component />);
  await user.press(screen.getByRole('button'));
  // ...
});

Custom Render

自定义渲染

Wrap providers using
wrapper
option:
tsx
function renderWithProviders(ui: React.ReactElement) {
  return render(ui, {
    wrapper: ({ children }) => (
      <ThemeProvider>
        <AuthProvider>{children}</AuthProvider>
      </ThemeProvider>
    ),
  });
}
使用
wrapper
选项包裹提供者组件:
tsx
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、迁移指南
  • 反模式 — 需要避免的常见错误