Playwright 的 VCR

录制一次。
永久回放。

在你的 Playwright 套件本地运行时捕获真实 API 响应,然后在 CI 上逐字节地回放它们。无需后端、无需网络,也无需维护手写 mock。

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

看它录制,再回放

一次 Playwright 运行把真实响应录制到磁盘;翻转到回放,同一套件在后端关闭的情况下通过 —— 无需网络。

两种录制器,一个代理

请求来自两个地方,因此有两种录制机制。使用其中一种 —— 或两者同时使用。两者都是录制一次并从磁盘回放,因此 CI 在后端关闭、没有手写 mock 的情况下运行。

代理

.mock.json

Next.js / TanStack Start SSR → 代理 → 真实 API

位于你的服务器与 API 之间。录制服务端请求 —— SSR fetch、route handler,以及你的 backend-for-frontend 所调用的任何内容。

适用于服务器调用 API 的全栈应用。

查看 Next.js 示例 → 查看 TanStack Start 示例 →

HAR

.har

浏览器 → HAR 拦截 → 真实 API

在浏览器内部进行拦截。录制客户端 fetch 调用、Chrome 扩展 API 流量、分析数据、第三方 API。

适用于 SPA、扩展和纯浏览器应用。

查看 Chrome 扩展示例 →

它适合什么场景

不同的 mock 工具擅长不同的工作。下面的组合 —— 跨 SSR、浏览器和 WebSocket 录制真实流量,且不手写 mock —— 正是其他工具留下的空白。

test-proxy-recorder 与 Playwright routeFromHAR、MSW、Polly.js、playwright-network-cache 和 Mocky Balboa 的特性对比。
特性 test-proxy-recorder Playwright routeFromHAR MSW Polly.js playwright-network-cache Mocky Balboa
录制真实流量
服务端(SSR) 部分
浏览器端
WebSocket
Playwright 原生
维护中

Polly.js 拦截 Node HTTP,所以 SSR mock 可以在应用进程内部实现,但无法作为 Playwright 运行的一部分。MSW 和 Mocky Balboa 也能回放真实响应 —— 但你需要手写 mock。何时改用其他工具,参见 文档

与你的真实认证提供方配合工作

通过 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' });
});

三步完成搭建

用一条命令脚手架生成所有内容,把 API 指向代理,然后录制并提交。纯浏览器应用? init 会替你跳过 SSR 步骤。

最快路径:交给你的 AI agent

复制这段内容,换成你的后端 URL,然后粘贴到 Claude Code、Cursor 或任何编码 agent 中 —— 它会运行 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 fixture、一个全局 teardown、 package.json 脚本,并(在 Next.js 上)把 SSR fetch 打标接入你的 root layout —— 全程非破坏性。

    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 上, init 还会添加 registerProxyFetch() 到你的 root layout,给服务端的 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"

停止手写 mock

你的 API 已经给出正确答案。把它们录制下来。

npm i -D test-proxy-recorder 在 GitHub 上加星

如果它帮你省下了一个下午,点一颗 star 只要一秒钟 —— 这就是下一个人找到它的方式,也是在告诉一个独自维护的人继续做下去。遇到卡壳或有想法? 提交 issue加入 Discord