やあ。今日もReactのコードと睨めっこ、お疲れ様。
APIのモック戦略について悩んでいるんだね。テストコードを書いているとき、「本番のAPIサーバーがダウンしているからテストが落ちる」「データが不安定でテスト結果がぶれる」といった状況に陥っていないか?
もしそうなら、君が悪いんじゃない。それは「ネットワークという不確実性」にテストを依存させている設計が原因だ。今日は、世界中のシニア層がこぞって採用している MSW (Mock Service Worker) を使った、堅牢で「泥臭くない」テスト戦略について、現場の知見を詰め込んで話そうと思う。
—
なぜ、今さら「MSW」なのか?
昔は `jest.mock(‘axios’)` のようにライブラリ単位でモックするのが主流だった。だが、あれは地獄だ。axiosからfetchに乗り換えただけでテストが全滅するし、何より「実際にネットワークリクエストが行われている感覚」がテストから消えてしまう。
MSWが優れているのは、ブラウザの Service Worker API を利用して、ネットワークリクエストを「横取り(インターセプト)」するところにある。Reactコンポーネント側は、それが本物のバックエンドなのか、MSWが返したモックなのかを一切気にしなくていい。これこそが、テストにおける関心の分離だ。
—
1. MSWの設定:まずは「司令塔」を作る
まずは、プロジェクトのルートにモックのハンドラー(司令塔)を定義しよう。
// src/mocks/handlers.ts
import { http, HttpResponse } from ‘msw’;
export const handlers = [
// GETリクエストをインターセプト
http.get(‘/api/user’, () => {
return HttpResponse.json({
id: ‘1’,
name: ‘伝説のエンジニア’,
});
}),
// POSTリクエストでエラーをシミュレート
http.post(‘/api/login’, () => {
return new HttpResponse(null, { status: 401 });
}),
];
ここで重要なのは、`http` メソッドを使うことだ。MSW v2以降は `rest` ではなく `http` を使うのが作法だ。これだけで、REST APIだろうがGraphQLだろうが、フロントエンドの通信を網羅できる。
—
2. テスト環境への注入:サーバーを立ち上げる
次に、VitestやJestの環境でMSWを起動させる設定だ。`setupTests.ts` あたりに書くのが一般的だな。
// src/setupTests.ts
import { server } from ‘./mocks/server’;
// テスト開始前にモックサーバーを起動
beforeAll(() => server.listen());
// 各テストの後にリクエストハンドラーをリセット(テスト間の干渉を防ぐ)
afterEach(() => server.resetHandlers());
// テスト終了後にサーバーを停止
afterAll(() => server.close());
こうすることで、テストコードが実行されるたびにクリーンなネットワーク環境が用意される。これが「再現性の高いテスト」の第一歩だ。
—
3. 実践:Reactコンポーネントのテスト
さて、実際にどうテストを書くか。React Testing Libraryと組み合わせるのが王道だ。
// UserProfile.test.tsx
import { render, screen, waitFor } from ‘@testing-library/react’;
import UserProfile from ‘./UserProfile’;
test(‘APIから取得したユーザー名が表示されること’, async () => {
render(
// ローディング状態を確認
expect(screen.getByText(/読み込み中/i)).toBeInTheDocument();
// MSWがインターセプトして返したデータが表示されるのを待つ
const userName = await screen.findByText(‘伝説のエンジニア’);
expect(userName).toBeInTheDocument();
});
見ての通り、テストコードの中にAPIのURLやレスポンスの構造を直接書く必要はない。コンポーネントはただ「データが来たら表示する」ことに集中し、テストは「表示されたか」を確認する。これが本来のReactのコンポーネントテストの姿だ。
—
現場で役立つ「泥臭い」Tips
ここで、現場のシニアとして一つアドバイスを贈ろう。
- エラー系のテストを怠るな:
「成功系」のテストは誰でも書く。だが、`server.use()` を使えば、特定のテストケースだけで一時的にハンドラーを上書きして「500エラー」や「404エラー」を強制できる。
test(‘サーバーエラー時に適切なメッセージが出るか’, async () => {
server.use(
http.get(‘/api/user’, () => new HttpResponse(null, { status: 500 }))
);
render(
expect(await screen.findByText(/エラーが発生しました/i)).toBeInTheDocument();
});
- MSW Browser Mocking:
MSWはテスト環境だけじゃない。開発中に `npx msw init public/` を実行してService Workerを配置すれば、バックエンドが未完成でも、ブラウザ上で実際に通信が発生する状態で開発を進められる。 これを導入すると、開発速度が劇的に変わるぞ。
—
最後に:なぜこれにこだわるのか
テストとは、単なる「バグ発見機」ではない。君が書いたコードの「仕様書」であり、チームメンバーへの「安全装置」だ。
MSWを使ってネットワークをモックするということは、君のReactコンポーネントから「不安定な要素」を剥ぎ取り、純粋なUIロジックとして検証できるようにする行為なんだ。この設計思想を身につければ、君はもう単なるコーダーじゃない。システム全体を俯瞰できるアーキテクトに一歩近づいたと言っていい。
さあ、エディタに戻って、テストコードを書き換えようか。何か詰まったら、いつでも聞いてくれ。応援しているよ。

コメント