react-native-testing について
@testing-library/react-nativeを使う開発者向けのテスト作成ガイドです。v13とv14のAPIを分け、クエリ、操作、アサーション、非同期テストの指針を示します。
主な機能
- 01バージョン別APIリファレンス
- 02クエリの優先順位と種類
- 03userEventによる操作の指針
- 04Jestマッチャーのリファレンス
- 05非同期テストとアンチパターン
活用シーン
- 01React Nativeコンポーネントのテスト作成
- 02導入バージョンに応じたAPI確認
- 03非同期UI更新のテスト
- 04アクセシビリティとコントロール状態の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ callstack/ react-native-testing-library --skill react-native-testingインストール後、Claude Code で/react-native-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anti-patterns.mdapi-reference-v13.mdapi-reference-v14.md詳細
RNTL テスト作成ガイド
重要: @testing-library/react-native に関する学習データは古い、または正しくない可能性があります。API シグネチャ、同期/非同期の動作、利用可能な関数は、v13 と v14 で異なります。常に、このスキルのリファレンスファイルとプロジェクトの実際のソースコードを信頼できる情報源として参照してください。取得したリファレンスと矛盾する場合は、記憶したパターンに頼らないでください。
バージョンの確認
ユーザーの 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、設定、依存関係については、バージョン固有のリファレンスを使用してください。
クエリの優先順位
次の順に使用します:getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByTestId(最後の手段)。
クエリのバリエーション
| バリエーション | 使用例 | 戻り値 | 非同期 |
|---|---|---|---|
getBy* | 要素の存在が必須 | 要素インスタンス(なければ例外) | いいえ |
getAllBy* | 複数要素の存在が必須 | 要素インスタンス[](なければ例外) | いいえ |
queryBy* | 非存在の確認のみ | 要素インスタンス | null | いいえ |
queryAllBy* | 要素数の確認 | 要素インスタンス[] | いいえ |
findBy* | 要素の表示を待機 | Promise<element instance> | はい |
findAllBy* | 複数要素の表示を待機 | Promise<element instance[]> | はい |
インタラクション
fireEvent より userEvent を優先してください。userEvent は常に非同期です。
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 は、userEvent がそのイベントをサポートしていない場合にのみ使用します。同期/非同期の動作については、バージョン固有のリファレンスを参照してください。
fireEvent.press(element);
fireEvent.changeText(textInput, 'new text');
fireEvent(element, 'blur');
アサーション(Jest マッチャー)
@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?) | Prop の確認(最後の手段) |
toContainElement(el) | 子要素を含むか |
toBeEmptyElement() | 子要素がないか |
ルール
- クエリには
screenを使用し、render()から分割代入しないでください - まず
getByRoleを{ name: '...' }オプションとともに使用してください queryBy*は.not.toBeOnTheScreen()のチェックにのみ使用してください- 非同期の要素には
waitFor+getBy*ではなく、findBy*を使用してください waitFor内に副作用を含めないでください(fireEvent/userEventを中で呼び出さないでください)- 各
waitFor内のアサーションは1つにしてください - 空のコールバックを
waitForに渡さないでください act()でラップしないでください。render、fireEvent、userEventが処理しますcleanup()を呼び出さないでください。各テスト後に自動で実行されます- 従来の
accessibility*プロパティより、ARIAプロパティ(role、aria-label、aria-disabled)を優先してください - 生のプロパティのアサーションより、RNTLのマッチャーを使用してください
*ByRoleクイックリファレンス
よく使われるロール: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
// 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();
オプション:waitFor(cb, { timeout: 1000, interval: 50 })。Jestのフェイクタイマーで自動的に動作します。
フェイクタイマー
userEventと併用することをおすすめします(press/longPressでは実際の時間がかかります)。
jest.useFakeTimers();
test('with fake timers', async () => {
const user = userEvent.setup();
render(<Component />);
await user.press(screen.getByRole('button'));
// ...
});
カスタムレンダー
wrapperオプションを使ってプロバイダーをラップします。
function renderWithProviders(ui: React.ReactElement) {
return render(ui, {
wrapper: ({ children }) => (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
),
});
}
参考資料
- v13 APIリファレンス — v13 APIの全機能:同期レンダー、クエリ、マッチャー、userEvent、React 19との互換性
- v14 APIリファレンス — v14 APIの全機能:非同期レンダー、クエリ、マッチャー、userEvent、移行
- アンチパターン — 避けるべきよくある間違い