react-native-testing

react-native-testing

熱門

使用 React Native Testing Library (RNTL) v13 與 v14 (`@testing-library/react-native`) 撰寫測試。適用於撰寫、審查或修復 React Native 元件測試。涵蓋範圍包括:render、screen、查詢(getBy/getAllBy/queryBy/findBy)、Jest matchers、userEvent、fireEvent、waitFor 以及非同步模式。支援 v13(React 18,同步 render)與 v14(React 19+,非同步 render)。觸發時機:React Native 元件的測試檔案、匯入 RNTL、提及 "testing library"、"write tests"、"component tests" 或 "RNTL"。

3403星標
281分支
更新於 2026/7/21
SKILL.md
唯讀
名稱
react-native-testing
描述

使用 React Native Testing Library (RNTL) v13 與 v14 (`@testing-library/react-native`) 撰寫測試。適用於撰寫、審查或修復 React Native 元件測試。涵蓋範圍包括:render、screen、查詢(getBy/getAllBy/queryBy/findBy)、Jest matchers、userEvent、fireEvent、waitFor 以及非同步模式。支援 v13(React 18,同步 render)與 v14(React 19+,非同步 render)。觸發時機:React Native 元件的測試檔案、匯入 RNTL、提及 "testing library"、"write tests"、"component tests" 或 "RNTL"。

RNTL 測試撰寫指南

重要事項: 你訓練資料中關於 @testing-library/react-native 的內容可能已過時或不正確 — API 簽名、同步/非同步行為以及可用函式在 v13 與 v14 之間存在差異。請務必以本 Skill 的參考文件及專案實際的原始碼作為權威依據(source of truth)。當記憶中的撰寫模式與檢索到的參考內容衝突時,切勿依賴記憶中的寫法。

版本判斷

檢查使用者 package.json@testing-library/react-native 的版本:

請依據特定版本的參考文件,來使用 render 模式、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() 元素可見(未隱藏/非 display:none
toBeEnabled() / toBeDisabled() 透過 aria-disabled 確認停用狀態
toBeChecked() / toBePartiallyChecked() 勾選狀態
toBeSelected() 選取狀態
toBeExpanded() / toBeCollapsed() 展開狀態
toBeBusy() 忙碌狀態
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. 僅在進行 .not.toBeOnTheScreen() 檢查時使用 queryBy*
  4. 非同步元素使用 findBy*,而非 waitFor + getBy*
  5. 絕不在 waitFor 內放置副作用(內部不得呼叫 fireEvent/userEvent
  6. 每個 waitFor 只放一個斷言
  7. 絕不傳入空的 callback 給 waitFor
  8. 不要包裹在 act()renderfireEventuserEvent 已自動處理
  9. 不要呼叫 cleanup() — 每次測試後會自動執行
  10. 優先使用 ARIA propsrolearia-labelaria-disabled),而非舊版的 accessibility* props
  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 使用(按壓 / 長按包含真實時間延遲):

jest.useFakeTimers();

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

自訂 Render

使用 wrapper 選項包裹 Provider:

function renderWithProviders(ui: React.ReactElement) {
  return render(ui, {
    wrapper: ({ children }) => (
      <ThemeProvider>
        <AuthProvider>{children}</AuthProvider>
      </ThemeProvider>
    ),
  });
}

參考文件