mcpField
R
react-native-testing

react-native-testing

github.com/callstackテスト・品質3.4k5.3k 回インストール1 週間前に更新
リポジトリ

react-native-testing について

@testing-library/react-nativeを使う開発者向けのテスト作成ガイドです。v13とv14のAPIを分け、クエリ、操作、アサーション、非同期テストの指針を示します。

主な機能

  1. 01バージョン別APIリファレンス
  2. 02クエリの優先順位と種類
  3. 03userEventによる操作の指針
  4. 04Jestマッチャーのリファレンス
  5. 05非同期テストとアンチパターン

活用シーン

  1. 01React Nativeコンポーネントのテスト作成
  2. 02導入バージョンに応じたAPI確認
  3. 03非同期UI更新のテスト
  4. 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.md
api-reference-v13.md
api-reference-v14.md

詳細

RNTL テスト作成ガイド

重要: @testing-library/react-native に関する学習データは古い、または正しくない可能性があります。API シグネチャ、同期/非同期の動作、利用可能な関数は、v13 と v14 で異なります。常に、このスキルのリファレンスファイルとプロジェクトの実際のソースコードを信頼できる情報源として参照してください。取得したリファレンスと矛盾する場合は、記憶したパターンに頼らないでください。

バージョンの確認

ユーザーの package.json で @testing-library/react-native のバージョンを確認します。

レンダリングパターン、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()子要素がないか

ルール

  1. クエリにはscreenを使用し、render()から分割代入しないでください
  2. まずgetByRoleを{ name: '...' }オプションとともに使用してください
  3. queryBy*は.not.toBeOnTheScreen()のチェックにのみ使用してください
  4. 非同期の要素にはwaitFor + getBy*ではなく、findBy*を使用してください
  5. waitFor内に副作用を含めないでください(fireEvent/userEventを中で呼び出さないでください)
  6. 各waitFor内のアサーションは1つにしてください
  7. 空のコールバックをwaitForに渡さないでください
  8. act()でラップしないでください。render、fireEvent、userEventが処理します
  9. cleanup()を呼び出さないでください。各テスト後に自動で実行されます
  10. 従来のaccessibility*プロパティより、ARIAプロパティ(role、aria-label、aria-disabled)を優先してください
  11. 生のプロパティのアサーションより、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>
    ),
  });
}

参考資料

コメント