これらの問題を解決し、実装に依存せず、かつ本番に近い環境でテストを行うためのデファクトスタンダードが MSW (Mock Service Worker) です。
この記事では、Next.js環境におけるMSWのセットアップから、実務で役立つ高度なモック手法までを徹底解説します。
1. MSWとは?なぜ他の手法より優れているのか#
MSW(Mock Service Worker)は、ネットワークレベルでHTTPリクエストをインターセプト(横取り)してモックするライブラリです。
モック手法の比較#
| 手法 | インターセプト層 | メリット | デメリット |
|---|---|---|---|
| jest.mock / spyOn | 言語レベル (JS/TS) | 設定が簡単 | 実装コードに強く依存する |
| nock | Node.js httpモジュール | 実装への依存が少ない | ブラウザ環境で動かない |
| MSW | ネットワークレベル | 実装・環境を問わず、本番に近い挙動 | 初期設定がやや複雑 |
MSWはブラウザでは Service Worker を、Node.js環境では インターセプター を使用するため、同じハンドラをテスト環境(Vitest/Jest)と開発環境(Storybook/ブラウザ)で共有できるという圧倒的な強みがあります。
2. MSWの仕組み:どうやって通信を横取りするのか#
MSWは実際のネットワークリクエストが外に出る直前でキャッチします。
- コンポーネントが
fetch("/api/user")を実行。 - MSWがリクエストを検知。
- 定義された Handler にURLとメソッドが一致するものがあるか確認。
- 一致すれば、定義された モックレスポンス を返却。
これにより、コンポーネント側は「実際のAPIと通信している」と完全に信じ込んだ状態で動作します。
3. 【実践】Next.js環境への導入手順#
最新の MSW v2 を前提としたセットアップを紹介します。
インストール#
npm install msw --save-devハンドラ(Handlers)の定義#
ドメインごとに分割して管理するのが保守性を高めるコツです。
// src/mocks/handlers/user.ts
import { http, HttpResponse } from "msw";
export const userHandlers = [
http.get("/api/user", () => {
return HttpResponse.json({
id: "u-001",
name: "テストユーザー",
email: "[email protected]"
});
}),
];テスト環境(Node.js)の設定#
// src/mocks/node.ts
import { setupServer } from "msw/node";
import { userHandlers } from "./handlers/user";
export const server = setupServer(...userHandlers);グローバル設定(Vitest / Jest)#
テストごとにハンドラをリセットし、テスト間の干渉を防ぎます。
// vitest.setup.ts
import { beforeAll, afterAll, afterEach } from "vitest";
import { server } from "./src/mocks/node";
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());4. 高度なモックテクニック#
実務では「正常系」だけでなく、様々なケースをシミュレートする必要があります。
4.1 テストごとにレスポンスを上書きする#
特定のテストケースだけエラー(500)を返したい場合は server.use() を使います。
test("サーバーエラー時にエラーメッセージが表示されること", async () => {
server.use(
http.get("/api/user", () => {
return new HttpResponse(null, { status: 500 });
})
);
render(<UserPage />);
expect(await screen.findByText("通信エラーが発生しました")).toBeInTheDocument();
});4.2 ローディング(遅延)のシミュレーション#
非同期処理の待機中のUIを確認したい場合、delay を使用します。
http.get("/api/user", async () => {
await delay(1000); // 1秒遅延
return HttpResponse.json({ name: "遅延ユーザー" });
});5. Next.js App Router での注意点#
Next.js 13+ の Server Components (RSC) で fetch を使用する場合、コードはサーバーサイド(Node.js環境)で実行されます。このため、MSWもブラウザ用の setupWorker ではなく、Node用の setupServer が適用されている必要があります。
Next.jsのネイティブ fetch は、デフォルトでキャッシュが効くことがあります。テスト環境ではキャッシュの影響を排除するため、必要に応じてキャッシュ無効化の設定を確認してください。
6. ベストプラクティス:型安全なモック#
TypeScriptを使用している場合、APIの型定義をMSWのハンドラでも活用しましょう。
import { http, HttpResponse, PathParams } from "msw";
import { UserType } from "@/types/user";
export const handler = http.get<PathParams, never, UserType>(
"/api/user",
() => {
return HttpResponse.json({ id: "1", name: "Safe User" });
}
);型を適用することで、APIのレスポンス形式が変更された際にテストコード(モック)側の不整合にいち早く気づくことができます。
7. まとめ#
MSWを導入することで、フロントエンドの統合テストは「外部への依存」から解放され、高い信頼性と保守性を手に入れることができます。
- ネットワークレイヤーでモック するため、実装(axios / fetch)に左右されない。
- ハンドラの共有 により、Storybookや開発環境でも同じモックが使える。
- server.use による柔軟なレスポンス制御。
モダンなNext.js開発において、MSWはもはや「選択肢」ではなく「必須」のツールと言えるでしょう。











