web-mocks-msw について
web-mocks-mswは、開発やテストでMSWを使ってネットワークリクエストをモックするガイドスキルです。ブラウザーとNodeでハンドラーを共有し、応答バリエーションやテスト単位の上書きを管理する方法を示します。
主な機能
- 01ブラウザーとNodeでハンドラーを共有
- 02再利用可能な型付きレスポンスデータ
- 03標準・空・エラーの応答バリエーション
- 04テスト単位のハンドラー上書き
- 05遅延と実行時バリエーションの設定
活用シーン
- 01バックエンド完成前のAPIモック
- 02空・エラー・遅延応答のテスト
- 03テストごとに特定エンドポイントを上書き
- 04開発中の応答バリエーション切り替え
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-mocks-mswインストール後、Claude Code で/web-mocks-mswを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlexamples/browser.mdcore.md詳細
MSW パターン
クイックガイド: MSW はネットワーク層でリクエストをインターセプトするため、アプリケーションコードはモックされていることを認識しません。1 つのハンドラーセットを両方の環境で使用できます。開発では
msw/browserのsetupWorker、テストではmsw/nodeのsetupServerを使います。この 2 つは相互に置き換えられません。レスポンスボディは独立したモジュールに置き、各バリアント(デフォルト、空、エラー)を再利用可能にし、API の生成された型に対して型付けします。
詳細なリソース:
- examples/core.md — モックデータモジュール、バリアントハンドラー、サーバーのセットアップとテストのライフサイクル、テストごとのオーバーライド、実行時のバリアント切り替え、遅延のシミュレーション
- examples/browser.md — クライアントレンダリングおよびサーバーレンダリングのエントリーポイント向けのブラウザーワーカーのセットアップとアプリの統合
該当する方法
ハンドラーは共通で、異なるのはセットアップモジュールとライフサイクルだけです。
- 開発時のブラウザー —
msw/browserのsetupWorkerを使用し、アプリのレンダリング前に完了を待機します。実行時のバリアント切り替えを使って、UI の各状態を確認できます。examples/browser.md に従ってください。 - テスト時の Node —
msw/nodeのsetupServerを使用し、テストスイートの開始時にリクエストの待ち受けを開始して、各テストの間にリセットします。1 つのテストだけのオーバーライドにはserver.use()を使用します。examples/core.md に従ってください。
<critical_requirements>
MSW コードを書く前に
環境に合ったセットアップ関数を使用してください。 setupWorker はサービスワーカー API を必要とし、setupServer は Node のリクエスト層をパッチするため、逆の環境で使うと入れ替えが原因だとは分からず、不足しているグローバル変数を示すエラーが発生します。
afterEach で server.resetHandlers() を呼び出してハンドラーをリセットしてください。 これにより、server.use() のオーバーライドが次のテストの結果を左右するのを防げます。
レンダリング前に worker.start() の完了を待機してください。 ワーカーの準備が整う前に送信されたリクエストは実際のネットワークに到達するため、テストスイートの最初のレンダリングだけが、ほかのレンダリングと異なる結果になることがあります。
レスポンスボディは独立したモジュールに置き、API の生成された型に対して型付けしてください。 1 つのフィクスチャを複数のハンドラーで使用でき、スキーマの変更はアサーションの実行時ではなくコンパイル時に検出されます。
</critical_requirements>
自動検出: msw、setupWorker、setupServer、msw/browser、msw/node、http.get、http.all、HttpResponse.json、server.use、resetHandlers、onUnhandledRequest、mockServiceWorker.js、delay()
対象:
- バックエンドがまだない開発時に HTTP レスポンスをモックする
- アプリケーションコードを変更せずに、空のレスポンス、エラーレスポンス、遅いレスポンスを確認する
- 開発サーバーとテストスイートで 1 つのハンドラーセットを共有する
- 1 つのテストだけで単一のエンドポイントをオーバーライドする
対象外:
- テストランナーの設定とライフサイクルフック — このスキルでは
beforeAllとafterEachに何を記述するかを説明しますが、それらを提供するランナーについては扱いません - コンポーネントのレンダリングと結果のクエリ
- 実際のバックエンドとの統合。サーバー自体の動作がテスト対象である場合
MSW がモックするのは、呼び出し元のコードではなくネットワークです。アプリケーションに注入、ラップ、置換されるものはないため、テストで実行されるものは本番環境で実行されるものと同じです。また、同じハンドラーを開発サーバー、テストスイート、デモビルドで使用できます。
これは、ハンドラーセットが個々のテストのニーズではなく、API の説明であり続ける場合に限ります。データはフィクスチャに置き、ハンドラーはフィクスチャを選択します。個々のテストで必要な変更は、後で破棄されるオーバーライドを通じて適用します。
<decision_framework>
応答を切り替える仕組み
| 実現したいこと | 使用するもの |
|---|---|
| 複数のテストで共有するシナリオ | デフォルトの隣にエクスポートする名前付きのバリアントハンドラー |
| 1つのテストだけで異なる内容を確認する | server.use(variant()) — resetHandlers() で破棄されます |
| 開発中に手動で状態を切り替える | デフォルトのハンドラーがリクエスト時に読み取るバリアントマップ |
| テスト対象のコードが待機する応答 | 明示的な時間を指定した delay(ms) |
実行時のバリアント切り替えは開発時のみに使用します。テストでは、状態を設定したテストの後も残る共有の可変状態になります。一方、server.use() はスコープが限定され、自動的にクリーンアップされます。
</decision_framework>
基本パターン
パターン 1:レスポンスボディを専用モジュールに配置する
フィクスチャはハンドラーとは別に配置し、API の生成型に対して型付けします。これにより、1つのボディを複数のハンドラーで使い回せるほか、スキーマの変更が型エラーとして検出されます。
// mocks/features.ts
import type { GetFeaturesResponse } from "./api-types";
export const defaultFeatures: GetFeaturesResponse = {
features: [{ id: "1", name: "Dark mode", status: "done" }],
};
export const emptyFeatures: GetFeaturesResponse = { features: [] };
1つのテストに固有のデータは、そのテスト内に記述します。
全コード:examples/core.md
パターン 2:バリアントハンドラー
デフォルトのハンドラーと各代替シナリオを1つのモジュールからエクスポートすることで、テストと開発サーバーは名前でバリアントを選択できます。
import { http, HttpResponse } from "msw";
export const getFeaturesHandlers = {
defaultHandler: () =>
http.get(ENDPOINT, () => HttpResponse.json(defaultFeatures)),
emptyHandler: () =>
http.get(ENDPOINT, () => HttpResponse.json(emptyFeatures)),
errorHandler: () =>
http.get(ENDPOINT, () => new HttpResponse("Server error", { status: 500 })),
};
HttpResponse.json は JSON のコンテンツタイプを設定し、init.status で別のステータスが指定されない限り 200 を返すため、エラーのバリアントのみステータスコードを指定します。プレーンテキストや空のエラーなど、JSON 以外のボディには new HttpResponse(body, init) を使用します。
リクエスト時にバリアントマップを読み取るデフォルトのハンドラーを含む全コード:examples/core.md
パターン 3:1つのハンドラーセット、2つのセットアップ
ハンドラーの配列は共有し、それを使用するモジュールは環境に応じて選択します。
// browser-worker.ts
import { setupWorker } from "msw/browser";
export const browserWorker = setupWorker(...handlers);
// server-worker.ts
import { setupServer } from "msw/node";
export const server = setupServer(...handlers);
アプリとの連携を含む全コード:examples/browser.md
パターン 4:テストのライフサイクルとテストごとのオーバーライド
一度リッスンを開始し、テスト間でリセットし、最後に終了します。server.use() はハンドラーを先頭に追加し、そのハンドラーは resetHandlers() によって削除されます。
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it("renders the empty state", async () => {
server.use(getFeaturesHandlers.emptyHandler());
renderApp();
});
全コード:examples/core.md
<red_flags>
注意すべき点
実行時に問題が発生します:
- ブラウザーバンドルで
setupServerを使用する、または Node でsetupWorkerを使用する — エラーには不足しているグローバルが示されるため、インポートの取り違えではなく環境の問題に見えます worker.start()をawaitせずに呼び出す — 最初のリクエストでワーカーとの競合が起き、実際のネットワークに到達します。その結果、最初に実行される spec で断続的な失敗が発生します- サーバーサイドレンダリングされるエントリーポイントでブラウザーワーカーをトップレベルでインポートする — サービスワーカーのコードがサーバーバンドルに入り、ビルドに失敗します。
typeof windowチェックの内側で動的にインポートしてください afterEachにresetHandlers()がない — オーバーライドがテスト後も残るため、単独では成功するテストスイートが、異なる順序で実行すると失敗しますworker.start()が環境ガードの内側にない — 本番バンドルでサービスワーカーが登録され、実際のユーザーにフィクスチャが提供されます
予想外の動作:
- 引数なしの
delay()はブラウザーではランダムな 100~400ms の待機になり、Node では無効化されます。そのため、一時停止が必要なテストでは時間を明示的に渡す必要があります server.use()によるオーバーライドはresetHandlers()まで残り、追加したテストが終了しても解除されませんhttp.all()はパスに対するすべてのメソッドに一致するため、誤った HTTP メソッドでリクエストを送っても成功応答が返りますonce: trueを指定すると、ハンドラーは1回のリクエストに一致した後に停止します。これは順次応答に必要な動作ですが、後続のリクエストが気付かれないままフォールスルーする原因にもなりますstart/listenにonUnhandledRequestを指定しないと、ハンドラーを記述していないリクエストは何も通知されずに通過します。そのため、モックの不足がバックエンドの障害のように見えます。"bypass"、"warn"、"error"のいずれかを指定する必要があり、これによって無通知、警告、致命的エラーのいずれになるかが決まります
</red_flags>