MSW(Mock Service Worker)
重要: msw に関するトレーニングデータは、古くなっているか誤っている可能性があります。MSW v2 では rest 名前空間、res(ctx.*) によるレスポンスの合成、(req, res, ctx) のリゾルバーシグネチャが完全に削除されました。MSW v3(2026-09-28)は ESM のみとなり、Node.js >=22 および TypeScript >=5.9 が必要です。常に、このスキルのルールファイルとプロジェクトの実際のソースコードを信頼できる情報源としてください。取得したリファレンスと矛盾する場合、記憶している v1 または v2 のパターンに頼らないでください。
MSW を使用する場面
MSW はネットワークレベルでの API モックに使用します。アプリケーションコードを変更せずに、テスト、Storybook、ローカル開発で HTTP、GraphQL、Server-Sent Events(sse)、WebSocket(ws)のトラフィックをインターセプトします。
| 必要なこと | 推奨ツール |
|---|
| API 統合のテスト(React、Vue、Node) | MSW |
| Storybook の API モック | MSW(ブラウザーワーカー) |
| バックエンドなしでのローカル開発 | MSW(ブラウザーワーカー) |
| 純粋関数の単体テスト | 通常のテストダブル |
| 実際の API を使用した E2E テスト | Playwright/Cypress によるネットワークインターセプト |
| モジュール内部のモック | vi.mock() / jest.mock() |
クイックリファレンス — v2 の要点
// Imports
import { http, HttpResponse } from 'msw/http'
import { graphql } from 'msw/graphql'
import { sse } from 'msw/sse'
import { ws } from 'msw/ws'
import { delay } from 'msw/utils/delay'
import { bypass } from 'msw/utils/bypass'
import { passthrough } from 'msw/utils/passthrough'
import { setupServer } from 'msw/node' // tests, SSR
import { setupWorker } from 'msw/browser' // Storybook, dev
// React Native: import { setupServer } from '@msw/react-native'
// Handler
http.get('/api/user/:id', async ({ request, params, cookies }) => {
return HttpResponse.json({ id: params.id, name: 'John' })
})
// Server lifecycle (tests)
beforeAll(() => server.listen({ onUnhandledFrame: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
// Per-test override
server.use(
http.get('/api/user/:id', () => new HttpResponse(null, { status: 500 }))
)
// Concurrent test isolation
it.concurrent('name', server.boundary(async () => {
server.use(/* scoped overrides */)
}))
優先度別のルールカテゴリ
| 優先度 | カテゴリ | 影響度 | プレフィックス | ルール数 |
|---|
| 1 | ハンドラー設計 | 重大 | handler- | 4 |
| 2 | セットアップとライフサイクル | 重大 | setup- | 3 |
| 3 | リクエストの読み取り | 高 | request- | 2 |
| 4 | レスポンスの構築 | 高 | response- | 3 |
| 5 | テストパターン | 高 | test- | 4 |
| 6 | GraphQL | 中 | graphql- | 2 |
| 7 | ユーティリティ | 中 | util- | 2 |
ルール全20件
ハンドラー設計(重大)
| ルール | ファイル | 概要 |
|---|
http 名前空間を使用する | handler-use-http-namespace.md | v2 では rest が削除されています。http.get()、http.post() を使用します |
| URL にクエリパラメーターを含めない | handler-no-query-params.md | 条件式内のクエリパラメーターは除去されるため、ハンドラーがパスに過剰にマッチします |
| v2 のリゾルバーシグネチャ | handler-resolver-v2.md | (req, res, ctx) ではなく ({ request, params, cookies }) を使用します |
| v2 のレスポンス構築 | handler-response-v2.md | res(ctx.json()) ではなく HttpResponse.json() を使用します |
セットアップとライフサイクル(重大)
| ルール | ファイル | 概要 |
|---|
| 正しいインポートパス | setup-import-paths.md | サーバーには msw/node、ワーカーには msw/browser を使用します |
| ライフサイクルフック | setup-lifecycle-hooks.md | 常に beforeAll/afterEach/afterAll のパターンを使用します |
| ファイル構成 | setup-file-organization.md | src/mocks/ 内に handlers、node、browser のファイルを整理します |
リクエストの読み取り(高)
| ルール | ファイル | 概要 |
|---|
| イベント内でクローンする | request-clone-events.md | ライフサイクルイベント内で本文を読み取る前にリクエストをクローンします |
| 非同期で本文を読み取る | request-body-async.md | 本文の読み取りは非同期のため、必ず await request.json() を使用します |
レスポンスの構築(高)
| ルール | ファイル | 概要 |
|---|
| Cookie には HttpResponse を使用する | response-use-httpresponse.md | ネイティブの Response は Set-Cookie を破棄するため、HttpResponse を使用します |
| ネットワークエラー | response-error-network.md | HttpResponse.error() を使用し、通常の Error をスローしないでください |
| ストリーミング | response-streaming.md | Server-Sent Events には sse()、チャンク転送には ReadableStream を使用します |
テストパターン(高)
| ルール | ファイル | 概要 |
|---|
| リクエストではなく動作をテストする | test-behavior-not-requests.md | fetch 呼び出しの引数ではなく、UI や状態を検証します |
| テストごとのオーバーライド | test-override-with-use.md | エラーやエッジケースのテストでは server.use() を使用します |
| 並行実行時の分離 | test-concurrent-boundary.md | 並行テストを server.boundary() で囲みます |
| 未処理のリクエスト | test-unhandled-request.md | onUnhandledFrame: 'error' を設定します |
GraphQL(中)
| ルール | ファイル | 概要 |
|---|
| レスポンスの形式 | graphql-response-shape.md | HttpResponse.json を使って { data } / { errors } を返します |
| エンドポイントのスコープ設定 | graphql-scope-with-link.md | 複数の GraphQL API には graphql.link(url) を使用します |
ユーティリティ(中)
| ルール | ファイル | 概要 |
|---|
| bypass と passthrough | util-bypass-vs-passthrough.md | bypass() = 新しいリクエスト、passthrough() = 通過させます |
| delay の動作 | util-delay-behavior.md | Node.js では delay() と delay('real') による遅延を観測できません。明示的にミリ秒を指定してください |
レスポンスメソッドのクイックリファレンス
| メソッド | 用途 |
|---|
HttpResponse.json(data, init?) | JSON レスポンス |
HttpResponse.text(str, init?) | プレーンテキスト |
HttpResponse.html(str, init?) | HTML コンテンツ |
HttpResponse.xml(str, init?) | XML コンテンツ |
HttpResponse.formData(fd, init?) | フォームデータ |
HttpResponse.arrayBuffer(buf, init?) | バイナリデータ |
HttpResponse.error() | ネットワークエラー |
v1 から v2 への移行クイックリファレンス
| v1 | v2 |
|---|
import { rest } from 'msw' | import { http, HttpResponse } from 'msw' |
rest.get(url, resolver) | http.get(url, resolver) |
(req, res, ctx) => res(ctx.json(data)) | () => HttpResponse.json(data) |
req.params | resolver info の params |
req.body | await request.json() |
req.cookies | resolver info の cookies |
res.once(...) | http.get(url, resolver, { once: true }) |
res.networkError() | HttpResponse.error() |
ctx.delay(ms) | await delay(ms) |
ctx.data({ user }) | HttpResponse.json({ data: { user } }) |
v2 から v3 への移行クイックリファレンス
MSW 3.0.0(2026-09-28)はESM 専用で、Node.js >=22 および TypeScript >=5.9 が必要です(v6/v7 もサポートされています)。主な変更点は、すべてのエクスポートがパッケージのルートからサブパスのエントリポイントに移動したことです。完全なガイド(および公式 codemod)については、references/migration-v2-to-v3.md を参照してください。
| v2 | v3 |
|---|
import { http, HttpResponse } from 'msw' | import { http, HttpResponse } from 'msw/http' |
import { graphql } from 'msw' | import { graphql } from 'msw/graphql' |
import { ws } from 'msw' / import { sse } from 'msw' | msw/ws / msw/sse |
import { delay } from 'msw' | import { delay } from 'msw/utils/delay' |
import { bypass, passthrough } from 'msw' | msw/utils/bypass, msw/utils/passthrough |
名前空間での graphql.query(...) | 削除 — link-first:graphql.link(url).query(...) |
import { setupServer } from 'msw/native' | @msw/react-native パッケージが msw/native エントリポイントに置き換わります |
handleRequest() | 削除 — getResponse() を使用します(またはカスタムネットワークには実験的な defineNetwork() を使用します) |
onUnhandledRequest: 'error' | onUnhandledFrame: 'error' — リクエストと WebSocket 接続の両方を対象とします |
カスタムコールバック (request, print) | 単一の { frame, defaults } オブジェクト。frame.protocol で絞り込み、defaults.warn() を呼び出します |
server.events.on('connection', ...) | server.events.on('websocket:connection', ...) — ライフサイクル API のみ。ws リンク独自の addEventListener('connection') の名前は変わりません |
GraphQLLinkHandlers 型 | GraphQLLink に改名 |
msw の LifeCycleEventsMap | msw/experimental のプロトコル別マップ(HttpNetworkFrameEventMap、WebSocketNetworkFrameEventMap) |
worker.stop() が void を返す | Promise を返します — 実行中の重要なシナリオでは await worker.stop() を使用します |
v3 の新機能:msw/vite プラグイン(ワーカースクリプトを提供するため、mockServiceWorker.js を public にコピーする必要がなくなります)、GraphQL v17 と link-first サブスクリプション、WebSocket Extension API、ページ遷移とフォーム送信のインターセプト、passthrough リクエストヘッダーの変更、小さなバンドル向けのユーティリティごとの msw/utils/* モジュール。
参考資料
| 参考資料 | 内容 |
|---|
handler-api.md | http.* および graphql.* メソッド、URL 判定関数、パスパラメーター |
response-api.md | HttpResponse クラス、すべての静的メソッド、Cookie の処理 |
server-api.md | setupServer/setupWorker、ライフサイクルイベント、boundary() |
test-patterns.md | Vitest/Jest のセットアップ、オーバーライド、並行実行時の分離、キャッシュのクリア |
migration-v1-to-v2.md | v1 から v2 への破壊的変更と移行ガイドの全内容 |
migration-v2-to-v3.md | v2 から v3 への破壊的変更、新しい API、移行ガイドの全内容 |
anti-patterns.md | よくある 10 の間違いと、BAD/GOOD の例 |