
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"。
使用 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 的版本:
- 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)
請依據特定版本的參考文件,來使用 render 模式、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() |
元素可見(未隱藏/非 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() |
無子元素 |
規則
- 查詢時使用
screen,切勿從render()解構 - 優先使用
getByRole並搭配{ name: '...' }選項 - 僅在進行
.not.toBeOnTheScreen()檢查時使用queryBy* - 非同步元素使用
findBy*,而非waitFor+getBy* - 絕不在
waitFor內放置副作用(內部不得呼叫fireEvent/userEvent) - 每個
waitFor只放一個斷言 - 絕不傳入空的 callback 給
waitFor - 不要包裹在
act()中 —render、fireEvent、userEvent已自動處理 - 不要呼叫
cleanup()— 每次測試後會自動執行 - 優先使用 ARIA props(
role、aria-label、aria-disabled),而非舊版的accessibility*props - 優先使用 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 使用(按壓 / 長按包含真實時間延遲):
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>
),
});
}
參考文件
- v13 API 參考 — 完整 v13 API:同步 render、查詢、matchers、userEvent、React 19 相容性
- v14 API 參考 — 完整 v14 API:非同步 render、查詢、matchers、userEvent、遷移指南
- 反模式(Anti-Patterns) — 應避免的常見錯誤





