首页 / Playwright 入门教程 / 模拟 API 响应与 Mock

Playwright 入门教程

模拟 API 响应与 Mock

本教程共 59 篇 · 第 31 篇 · 更新于 2026-08-04 · 约 11 分钟阅读

PlaywrightMock接口模拟fulfillHAR浏览器API

31. 模拟 API 响应与 Mock

本节目标:学完能用一个假的接口响应替掉真实后端,让测试不依赖网络也能稳定通过。

上一章讲了怎么拦请求。拦下来之后呢?最常见的一招是:自己造一份假数据还回去,让页面以为接口真的返回了。这招在 Playwright 里叫 Mock(模拟)。

为什么要用 Mock

真实接口常常不靠谱。开发环境后端没写好,测试就卡住了;接口偶发超时,你的测试就误报红。Mock 把这些不确定性一刀切掉——接口返回什么,你说了算。

好处很直接:测试快、稳定、还能造出真实环境难触发的边界数据,比如一个超长的用户名。

Note

Mock 的核心是 route.fulfill(),它直接给浏览器一个响应,请求压根不会发到服务器。

用 fulfill 返回假数据

下面这个例子拦截了水果列表接口,返回一条假数据。注意要写在 page.goto 之前,先布好陷阱。

import { test, expect } from '@playwright/test';

test('用假数据替代接口', async ({ page }) => {
  // 先拦截接口,再打开页面
  await page.route('*/**/api/v1/fruits', async route => {
    const json = [{ name: 'Strawberry', id: 21 }];
    await route.fulfill({ json });
  });

  await page.goto('https://demo.playwright.dev/api-mocking');
  // 页面里应该出现我们造的 Strawberry
  await expect(page.getByText('Strawberry')).toBeVisible();
});

fulfill 支持 jsonbodystatusheaderscontentTypepath 等字段。返回 JSON 时直接用 json 最省事,Playwright 会顺手把 Content-Type 设好。

Tip

测试没通过,打开 Trace(追踪)看一眼回放。你会清楚看到接口根本没发出去,而是被 fulfill 拦下喂了假数据——这比对着代码干瞪眼快多了。

先调真接口,再改返回

有时你不想完全断网,只想给真实数据加点料。那就先 route.fetch() 拿到真响应,改完再 fulfill。

test('调真接口再加一条数据', async ({ page }) => {
  await page.route('*/**/api/v1/fruits', async route => {
    const response = await route.fetch();      // 真的去请求一次
    const json = await response.json();
    json.push({ name: 'Loquat', id: 100 });     // 往里塞一条
    await route.fulfill({ response, json });     // 用原响应,但换成新 json
  });

  await page.goto('https://demo.playwright.dev/api-mocking');
  await expect(page.getByText('Loquat', { exact: true })).toBeVisible();
});

这种「真请求 + 改返回」的套路,特别适合测「新增后列表刷新」这类逻辑。

用 HAR 文件批量录制

接口一多,一个个写 fulfill 就累了。HAR(HTTP Archive)文件能把页面跑过的所有请求录下来,测试时按记录回放。

录制一次:

// update: true 让 Playwright 真实发请求,并把结果写进 HAR
await page.routeFromHAR('./hars/fruit.har', {
  url: '*/**/api/v1/fruits',
  update: true,
});

录好之后把 update 关掉(去掉这一行或改成 false),测试就完全照 HAR 回放,不再碰真实接口。

想改假数据也不用重录:打开 hars 目录,里面有个哈希命名的 .txt 文件,直接编辑它里面的 JSON 就行。

Warning

HAR 回放对 URL 和 HTTP 方法卡得很严,POST 还会比对请求体。多个录制都能匹配时,挑请求头最像的那个。

Mock 浏览器自带 API

有些浏览器能力,比如电量 API(Battery API),Playwright 没法直接自动化。这时用 page.addInitScript() 在页面加载前偷偷换掉它。

test.beforeEach(async ({ page }) => {
  await page.addInitScript(() => {
    const mockBattery = {
      level: 0.90,
      charging: true,
      chargingTime: 1800,
      dischargingTime: Infinity,
      addEventListener: () => {},
    };
    // 让页面拿到的永远是这份假电量
    window.navigator.getBattery = async () => mockBattery;
  });
});

碰到只读属性直接赋值不生效,用 Object.defineProperty 强行覆盖:

await page.addInitScript(() => {
  Object.defineProperty(navigator, 'onLine', { get: () => false });
});

想确认页面真的调用过某个 API,可以用 page.exposeFunction() 在页面里挂一个回调,把调用记录传回测试进程再断言。

Tip

Mock 浏览器 API 要趁早。页面往往在加载那一瞬间就调用了,所以必须写在 addInitScript 里,赶在页面脚本执行前生效。写在 goto 之后就晚了。

小结

  • Mock 的核心是 route.fulfill(),请求根本不发出去,响应由你说了算。
  • 拦截必须写在 page.goto() 之前,先布好陷阱再开页面。
  • 想在真数据上加料,用 route.fetch() 取回真响应,改完再 fulfill({ response, json })
  • 接口一多就上 HAR:routeFromHARupdate: true 录一次,之后关掉 update 回放。
  • 改 HAR 数据不用重录,编辑 hars 目录里那个哈希 .txt 文件即可。
  • 浏览器自带 API(电量、在线状态等)用 addInitScript 在加载前替换掉。
  • Mock 是双刃剑:又快又稳,但用多了就离真实环境越远。我的习惯是联调阶段用真接口,冒烟和回归阶段再用 Mock 提速。

下一章讲怎么反过来——不拦请求,而是静静看着它,然后对返回做断言。