Playwright の VCR

一度記録する。
ずっと再生する。

Playwright スイートをローカルで実行しながら実際の API レスポンスをキャプチャし、CI 上でバイト単位でそのまま再生します。バックエンド不要、ネットワーク不要、保守すべき手書きモックも不要。

npm i -D test-proxy-recorder GitHub でスターGitHub のスター数

MIT · TypeScript · Next.js と TanStack Start の SSR、SPA、Chrome 拡張に対応 · WebSocket 対応

REC · MODE=record · local run

saved e2e/recordings/todos.GET.mock.json

REPLAY · MODE=replay · CI

200 OK · 3 ms · zero network

記録、そして再生の様子を見る

1 回の Playwright 実行で実際のレスポンスをディスクに記録し、再生に切り替えれば同じスイートがバックエンドをオフにしたまま通過します — ネットワーク不要。

2 つのレコーダー、1 つのプロキシ

リクエストは 2 つの場所から発生するため、記録の仕組みも 2 つあります。どちらか、または両方を併用できます。どちらも一度記録してディスクから再生するため、CI はバックエンドをオフにしたまま手書きモックなしで実行されます。

プロキシ

.mock.json

Next.js / TanStack Start SSR → プロキシ → 実際の API

サーバーと API の間に位置します。サーバーサイドのリクエスト — SSR フェッチ、ルートハンドラー、バックエンド・フォー・フロントエンドが呼び出すあらゆるものを記録します。

サーバーが API を呼び出すフルスタックアプリ向け。

Next.js の例を見る →TanStack Start の例を見る →

HAR

.har

ブラウザ → HAR 傍受 → 実際の API

ブラウザ自体の中で傍受します。クライアントサイドの fetch呼び出し、Chrome 拡張の API トラフィック、アナリティクス、サードパーティ API を記録します。

SPA、拡張、ブラウザのみのアプリ向け。

Chrome 拡張の例を見る →

適している場面

モックツールはそれぞれ得意分野が異なります。以下の組み合わせ — SSR、ブラウザ、WebSocket にまたがって実際のトラフィックを記録し、手書きモックを必要としないこと — こそが、他のツールが埋められていない空白です。

test-proxy-recorder と Playwright の routeFromHAR、MSW、Polly.js、playwright-network-cache、Mocky Balboa との機能比較。
機能test-proxy-recorderPlaywright routeFromHARMSWPolly.jsplaywright-network-cacheMocky Balboa
実際のトラフィックを記録対応対応非対応対応対応非対応
サーバーサイド (SSR)対応非対応対応部分的非対応対応
ブラウザサイド対応対応対応対応対応対応
WebSocket対応非対応対応非対応非対応非対応
Playwright ネイティブ対応対応非対応非対応対応対応
メンテナンス状況対応対応対応非対応対応対応

Polly.js は Node の HTTP を傍受するため、SSR のモックはアプリプロセス内では可能ですが、Playwright 実行の一部としてはできません。MSW と Mocky Balboa も実際のレスポンスを再生します — ただしモックを手書きする必要があります。いつ他のツールを選ぶべきかは、ドキュメントで説明されています。

実際の認証プロバイダーと連携

Cognito、Auth0、Clerk、WorkOS を通じて、毎回の実行で本物のログインを行います。記録されるのはアプリの API だけで、認証はライブのまま、データはオフラインで再生されます。

// e2e/auth.setup.ts — log in for real, once. Never recorded.
import { test as setup } from '@playwright/test';
import { setProxyMode } from 'test-proxy-recorder';
setup('authenticate', async ({ page }) => {
await setProxyMode('transparent'); // login bypasses the recorder
await page.goto('/login');
await page.getByTestId('email').fill(process.env.TEST_EMAIL!);
await page.getByTestId('password').fill(process.env.TEST_PASSWORD!);
await page.getByTestId('signinButton').click();
await page.waitForURL('/dashboard');
// Reused by every test — they start already signed in.
await page.context().storageState({ path: 'e2e/.auth/state.json' });
});

3 ステップでセットアップ

1 つのコマンドですべてをスキャフォールドし、API をプロキシに向け、記録してコミットします。ブラウザのみのアプリですか? initは SSR の手順をスキップしてくれます。

最速の方法: AI エージェントに任せる

これをコピーし、バックエンド URL を差し替えて、Claude Code、Cursor、または任意のコーディングエージェントに貼り付けてください — エージェントは initを実行し、 initが表示するプロンプトに従って配線を完了させます。

Set up test-proxy-recorder for end-to-end tests in this project, then follow the
instructions that `init` prints. Run these commands:
npx @tanstack/intent@latest install
npm install --save-dev test-proxy-recorder
Then run init, passing this project's backend API base URL as the target — find
it yourself from the app's env/config (the URL the app calls in dev); don't
assume the default:
npx test-proxy-recorder init <your-backend-api-url> --port 8100 --dir ./e2e/recordings
Then complete the app-specific steps init prints: point the app's API base URL at
the proxy in dev/test only, tag server-side fetches (Next.js), add a smoke test,
and verify record → replay.

または手動で配線する:

  1. インストールとスキャフォールド

    init は、プロキシ設定、Playwright フィクスチャ、グローバルティアダウン、package.json のスクリプトを作成し、(Next.js では)SSR フェッチのタグ付けをルートレイアウトに配線します — 非破壊的に。

    Terminal window
    npm install --save-dev test-proxy-recorder
    # http://localhost:3002 is your API endpoint; 8100 is the proxy. Flags are optional.
    npx test-proxy-recorder init http://localhost:3002 --port 8100 --dir ./e2e/recordings
  2. アプリの API をプロキシに向ける

    initが推測できない唯一のものは、どの環境変数が API のベース URL を保持しているかです。レコーダーが有効な場合はプロキシを指し、それ以外の場合は実際のバックエンドを指します — プロキシは本番環境では決して動きません。

    // Point your app at the proxy when the recorder is enabled, at the real backend otherwise.
    // The proxy never runs in production — TEST_PROXY_RECORDER_ENABLED is set only for e2e.
    const API_BASE =
    process.env.NODE_ENV === 'production' && !process.env.TEST_PROXY_RECORDER_ENABLED
    ? 'https://api.example.com'
    : 'http://localhost:8100'; // proxy address from `init`
    const res = await fetch(`${API_BASE}/todos`);

    Next.js では、 initregisterProxyFetch()もルートレイアウトに追加して、サーバーサイドの fetch呼び出しをタグ付けします — 本番では no-op です:

    // app/layout.tsx — tag server-side fetches so SSR is recorded/replayed
    import { registerProxyFetch } from 'test-proxy-recorder/nextjs';
    registerProxyFetch(); // no-op in production unless TEST_PROXY_RECORDER_ENABLED=true
  3. 記録、コミット、再生

    MODE = 'record'に設定し、実際の API に対して一度実行し、その後 'replay'に切り替えてコミットします。記録は git に保存されます — これが CI を決定論的にするものです。gitignore に追加しないでください。

    e2e/my.test.ts
    import { test, expect } from '@playwright/test';
    import { playwrightProxy } from 'test-proxy-recorder';
    // Full-stack: the browser also talks to the proxy, so match its URL.
    // (Browser-only app? Match your real API domain instead, e.g. /api\.example\.com/.)
    const CLIENT_SIDE_URL = /localhost:8100/;
    // 'record' hits the real API and saves responses.
    // 'replay' serves them from disk — no network needed.
    const MODE = 'replay' as const;
    test.beforeEach(async ({ page }, testInfo) => {
    await playwrightProxy.before(page, testInfo, MODE, { url: CLIENT_SIDE_URL });
    });
    test('homepage loads', async ({ page }) => {
    await page.goto('/');
    await expect(page.getByText('Welcome')).toBeVisible();
    });
    Terminal window
    # 1. Set MODE = 'record' in your test file, run against the real API
    npx playwright test --ui # recordings written to e2e/recordings/
    # 2. Flip MODE back to 'replay' and commit the recordings
    git add e2e/recordings/
    git commit -m "add e2e recordings"

手書きモックをやめる

API はすでに正しい答えを返しています。それを記録しましょう。

npm i -D test-proxy-recorder GitHub でスター

午後の時間が節約できたなら、スターは 1 秒で済みます — 次の人が見つける手段であり、1 人で保守を続ける者への「作り続けて」という合図です。つまずいたりアイデアがあれば、 issue を開くDiscord に参加してください。