You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Playwright测试中使用nock模拟Node应用API请求失效求助

问题:Playwright测试中nock无法模拟Node应用的API请求

我正在为Node应用编写首个Playwright截图测试,应用会发起服务端API请求,希望用nock模拟API响应,让测试稳定、可预测且能离线运行。之前在mocha+supertest的旧测试套件里成功用过nock,但新的Playwright测试里nock完全不起作用,推测原因是Node应用是独立进程启动的。

旧的supertest成功测试代码

const supertest = require("supertest");
const app = setupMyEntireNodeExpressApp();
nock("https://my-api.com").get("/").reply(200, { fakeData:true });
supertest(app).get("/").expect(200).end((err, response) => { doSomething(); }) //让supertest启动Node应用并发起请求

nock不生效的Playwright测试代码(myTest.spec.js)

const { test, expect } = require("@playwright/test");
const http = require("http");
const app = setupMyEntireNodeExpressApp();
test("My test", async ({ page, context }) => {
  nock("https://my-api.com").get("/").reply(200, { fakeData:true });
  http.createServer(app).listen(); //启动Node应用
  await page.goto("/"); //通过Playwright发起请求
  await expect(page).toHaveScreenshot("myTest.png", { fullPage: true }); //通过Playwright截图
});

运行测试时,nock完全不生效,应用还是会调用真实的https://my-api.com接口。我推测问题出在nock运行在myTest.spec.js进程里,但createServer启动的应用是独立进程,二者不共享nock的拦截规则。

补充说明:我已经在playwright.config.js里设置了webServer.reuseExistingServer: false,因为是从测试文件启动Node应用;也试过移除http.createServer(app).listen(),改用webServer.reuseExistingServer: true和webServer.command: "node app.js"让Playwright启动服务器,但问题依旧——显然是myTest.spec.js和node app.js分属不同进程,nock的拦截规则没法跨进程生效。

旧测试里nock能生效,是因为把Node应用对象传给了supertest(app).request,应用和测试代码运行在同一个进程里。我希望能类似地把应用对象传给Playwright,但查了文档没找到相关选项。想问问有没有简便的解决办法?比如用createServer的其他变体启动应用?


解决方案

方案1:让Node应用和测试代码运行在同一进程

核心思路是不启动独立的Node进程,直接在测试进程内启动HTTP服务器,让Playwright访问本地服务器地址。这样nock的拦截规则会直接作用于同一进程内的应用请求。

修改后的测试代码:

const { test, expect } = require("@playwright/test");
const http = require("http");
const app = setupMyEntireNodeExpressApp();

test("My test", async ({ page }) => {
  // 在测试进程内启动服务器,监听系统分配的随机端口
  const server = http.createServer(app);
  await new Promise(resolve => server.listen(0, resolve));
  const baseUrl = `http://localhost:${server.address().port}`;

  // 此时nock和应用同进程,拦截规则生效
  nock("https://my-api.com")
    .get("/")
    .reply(200, { fakeData: true });

  try {
    await page.goto(baseUrl);
    await expect(page).toHaveScreenshot("myTest.png", { fullPage: true });
  } finally {
    // 测试结束后关闭服务器
    await new Promise(resolve => server.close(resolve));
  }
});

方案2:在应用的测试模式中加载nock规则

如果必须让应用运行在独立进程,可以在应用代码中添加测试模式判断,直接在应用进程内启动nock拦截。

步骤1:修改应用代码

// app.js
if (process.env.NODE_ENV === "test") {
  const nock = require("nock");
  // 加载测试所需的拦截规则,也可以从外部文件导入
  nock("https://my-api.com")
    .get("/")
    .reply(200, { fakeData: true });
}

步骤2:配置Playwright启动命令

// playwright.config.js
module.exports = {
  webServer: {
    command: "NODE_ENV=test node app.js",
    reuseExistingServer: false,
  },
};

方案3:用Playwright自带的网络拦截替代nock

Playwright内置了网络拦截功能,无需依赖nock,直接拦截应用的API请求(包括前端和服务端发起的请求,服务端请求需配置代理)。

示例代码:

const { test, expect } = require("@playwright/test");

test("My test", async ({ page, context }) => {
  // 拦截所有指向目标API的请求,返回模拟数据
  await context.route("https://my-api.com/*", async (route) => {
    await route.fulfill({
      status: 200,
      json: { fakeData: true },
    });
  });

  await page.goto("http://localhost:3000"); // 替换为你的应用本地地址
  await expect(page).toHaveScreenshot("myTest.png", { fullPage: true });
});

注:如果是服务端发起的API请求,需要让应用的HTTP请求走Playwright代理,可通过设置应用的HTTP_PROXY环境变量实现。


内容的提问来源于stack exchange,提问作者Drkawashima

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 21:12:46