Jest + React Testing Libraryで書く実践的ユニットテスト集
テスティングツール選択の盲点:なぜこの組み合わせが「標準」なのか
Reactプロジェクトのテスティングについて検索すると「Jest + React Testing Libraryを使えば最高」という記事ばかり目につきます。ただし、これは完全な回答ではありません。
実は、Vitestは現在Jestよりも多くのweekly npm downloadsを記録しており、89.7 million weekly downloadsで Jestの46.2 million weekly downloadsを上回る状況です。つまり、新規プロジェクトはVitest側に流れているのに、従来の記事は古い情報を繰り返しています。
では、なぜこの記事は「Jest + React Testing Library」に焦点を当てるのか。それは、Jestは長年の「ファミリーセダン」として安定性があり、特にエンタープライズReactやNodeスタックで一般的だからです。大規模なレガシープロジェクトはいまだにJestが占めており、移行コストの問題から多くの組織がこの組み合わせを継続しています。
Jest 30とReact Testing Library 16.3.2の現在地
Jest 30はタイムスタンプにより2025年mid-2025に新たにリリースされ、パフォーマンスとTypeScript サポート(TypeScript 5.4+の要件を含む)が改善されました。一方、React Testing Library は @testing-library/react 16.3.2として52.5 million weekly npm downloadsで公開されています。
実装レベルでは、この二つのツールは全く異なるレイヤーで動いているという理解が最も重要です。Jest はテストランナー・アサーション・モッキング基盤を提供し、React Testing Libraryはコンポーネントテストの「哲学」をもたらします。
「何をテストすべきか」という判断軸の欠落
一般的なテスト記事が落とす視点があります。それは「カバレッジ %」と「実際の品質」がまったく関係ないという事実です。
多くの組織はコード カバレッジに過度にフォーカスし、その結果開発者は意味のないテストを書いてシステムをゲーミングしているのです。80% のカバレッジを達成した結果、本当に価値のあるバグを逃してしまう。それは設定を誤っているのではなく、判断軸そのものがズレているからです。
React Testing Libraryが唱える基本原則は次のとおり:テストが実装の詳細ではなく、ユーザーがソフトウェアを使う方法に近いほど、より多くの信頼を与える。つまり、querySelector で DOM をまさぐるのではなく、ユーザーの視点から操作する。
実装例:フォーム送信テストで差がわかる
テストを書く前に、何をテストするかの筋を通しましょう。以下は、ユーザー認証フォームの例です。
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from './LoginForm';
describe('LoginForm', () => {
it('ユーザーがメール + パスワードを入力して送信ボタンをクリックすると、API呼び出しが発生する', async () => {
const user = userEvent.setup();
const { container } = render(<LoginForm />);
// ユーザーが見えるロールベースのクエリを使用
const emailInput = screen.getByRole('textbox', { name: /email/i });
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /log in/i });
// ユーザーと同じ方法でインタラクト
await user.type(emailInput, 'user@example.com');
await user.type(passwordInput, 'password123');
await user.click(submitButton);
// ユーザーが見える結果を検証:ローディング状態、またはサクセスメッセージ
await waitFor(() => {
expect(screen.getByText(/logging in/i)).toBeInTheDocument();
});
});
});このコードで重要なのは何か。querySelector('.email-input') や data-testid を使っていません。代わりに getByRole や getByLabelText を使うことで、テストが「ユーザーが実際に見ている、操作している部分」にフォーカスしています。
もし後からエンジニアが を にリネームしたとしても、ロールは変わらないのでテストは壊れません。テストが実装の細部に結合していないのです。
無料パートで学ぶべき3つの考え方
1. テストピラミッドの「土台」の役割を理解する
コンポーネントテストは実行が速く、書くコストが低く、最も細粒度のバグカテゴリ(フォーマット関数が誤った丸め方をしている、ボタンがコールバックを起動しない)をキャッチします。つまり、Jest + React Testing Libraryで書くテストは、E2Eテストの何倍も数多く、何倍も安価に品質を確保できるレイヤーだということです。
2. モッキングの「階層」を意識する
Jestは jest.mock() でモジュール全体をモック化でき、React Testing LibraryのマッチャーはDOM要素に基づきます。この二つの立場を混同すると、テストは複雑になります。有料パートではこの違いを具体的な例で示します。
3. スナップショットテストは最後の手段
Jestのスナップショット機能は便利ですが、濫用するとメンテナンスの沼になります。「このコンポーネントはこの構造を持つべき」という変更意図のない場合にだけ有効です。
続きで学ぶこと
有料パートでは、以下の実践的なテストパターンを実装コード付きで解説します:
フォーム検証のテスト(入力値チェック、非同期バリデーション)
API呼び出しをモックした非同期処理のテスト(MSW活用)
カスタムフックのテストパターン
複数コンポーネント連携のシナリオテスト
CI環境でのカバレッジ計測と実践的な目標設定
Jest + React Testing Libraryは、単なるツール選択ではなく「ユーザー中心のテスト思想」の選択です。その背景を理解した上で実装に進むことで、単なるテスト個数ではなく真に価値あるテストスイートが構築できます。
ここから先は
¥ 300
この記事が気に入ったらチップで応援してみませんか?
