使用 Playwright 测试中的 nock 模拟数据

huangapple go评论115阅读模式
英文:

Mocking data with nock in playwright test

问题

I'm writing my first Playwright Screenshot-test for a node application. The application makes serverside api requests and I want to mock the api responses so that my test becomes stable, predictable and can be run offline.

I will use nock to setup mocked data, because I've used nock in my old existing test suite (built on mocha + supertest) with great success.

But in my new Playwright-test I can't get nock to apply to my node app, probably because the node app is started as a separate process.

An old successful test file that uses 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(); }) //let supertest start the node app and make a request

The new playwright-test (myTest.spec.js) where the nock does not apply

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(); //start node app
  await page.goto("/"); //make a request via playwright
  await expect(page).toHaveScreenshot("myTest.png", { fullPage: true }); //take screenshot via playwright
}

When I run the playwright test, the nock is not being applied to my node app. The app calls https://my-api.com and receives a response from the actual api rather than my mock data.

My GUESS is that the problem is that nock is running within the myTest.spec.js process but createServer starts my app as a separate process.

PS: in my playwright.config.js I've set webServer.reuseExistingServer:false since I'm starting my node app from the test file.

I tried removing http.createServer(app).listen(); and instead using webServer.reuseExistingServer:true and webServer.command: "node app.js". This is a way to let Playwright start the server via the command. But that caused the same problem. In that case I guess the problem is that myTest.spec.js is a separate process while node app.js starts a new process that doesn't have access to the nocks.

With my old successful test I guess the nock works because I can pass my node app object into supertest(app).request so the app is not started as a separate process, but is running within the same process as the test code where the nock is also running.

I wish there was a way to pass my app object to playwright in a similar way, but after reading their docs it doesn't seem like that's an option.

Is there an easy way to solve this?

Perhaps an alternative way of starting my node app using variants of createServer.

英文:

I'm writing my first Playwright Screenshot-test for a node application. The application makes serverside api requests and I want to mock the api responses so that my test becomes stable, predictable and can be run offline.

I will use nock to setup mocked data, because I've used nock in my old existing test suite (built on mocha + supertest) with great success.

But in my new Playwright-test I can't get nock to apply to my node app, probably because the node app is started as a separate process.

An old successful test file that uses 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(); }) //let supertest start the node app and make a request

The new playwright-test (myTest.spec.js) where the nock does not apply

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(); //start node app
  await page.goto("/"); //make a request via playwright
  await expect(page).toHaveScreenshot("myTest.png", { fullPage: true }); //take screenshot via playwright
}

When I run the playwright test, the nock is not being applied to my node app. The app calls https://my-api.com and receives a response from the actual api rather than my mock data.

My GUESS is that the problem is that nock is running within the myTest.spec.js process but createServer starts my app as a separate process.

PS: in my playwright.config.js I've set webServer.reuseExistingServer:false since I'm starting my node app from the test file.

I tried removing http.createServer(app).listen(); and instead using webServer.reuseExistingServer:true and webServer.command: "node app.js". This is a way to let Playwright start the server via the command. But that caused the same problem. In that case I guess the problem is that myTest.spec.js is a separate process while node app.js starts a new process that doesn't have access to the nocks.

With my old successful test I guess the nock works because I can pass my node app object into supertest(app).request so the app is not started as a separate process, but is running within the same process as the test code where the nock is also running.

I wish there was a way to pass my app object to playwright in a similar way, but after reading their docs it doesn't seem like that's an option.

Is there an easy way to solve this?

Perhaps an alternative way of starting my node app using variants of createServer

答案1

得分: 0

以下是翻译好的内容:

简而言之,你无法这样做。根据 nock 文档

> Nock 可用于测试执行 HTTP 请求的模块。

这意味着 nock 实际上不会修补 HTTP 服务器实现,而是修补了 HTTP 客户端,而且由于你的 HTTP 客户端是底层浏览器 Playwright 运行的,nock 对此一无所知。

因此,为了测试 API,你需要使用 Playwright 提供的方法,例如:

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

test('should create a bug report', async ({ request }) => {
  const newIssue = await request.post('/repos/github-username/test-repo-1/issues', {
    data: {
      title: '[Bug] report 1',
      body: 'Bug description',
    },
  });

  expect(newIssue.ok()).toBeTruthy();

  const issues = await request.get('/repos/github-username/test-repo-1/issues');

  expect(issues.ok()).toBeTruthy();
  expect(await issues.json()).toContainEqual(expect.objectContaining({
    title: '[Bug] report 1',
    body: 'Bug description',
  }));
});

在此示例中,我们使用 APIRequestContext 类提供的 postget 方法发送 API 请求并验证服务器状态。

下一步是模拟路由本身

await page.route('https://example.com/api/data', (route) => {
  route.fulfill({
    status: 200,
    body: JSON.stringify({ message: 'Hello, World!' }),
  });
});

无论请求是源自 APIRequestContext 还是应用程序,route 都能拦截它。

不幸的是,这意味着你无法利用你已经熟悉的 nock API,但它实现了模拟 API 的目标。

英文:

In short, you cannot. From nock documentation:

> Nock can be used to test modules that perform HTTP requests in isolation.

What this means is that nock does not actually patch the HTTP server implementation, but it patches the HTTP client instead, and since your HTTP client is whatever the underlying browser Playwright is running, nock has no knowledge of that.

Therefore, in order to test the API, you will need to use Playwright provided methods, e.g.

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

test('should create a bug report', async ({ request }) => {
  const newIssue = await request.post('/repos/github-username/test-repo-1/issues', {
    data: {
      title: '[Bug] report 1',
      body: 'Bug description',
    },
  });

  expect(newIssue.ok()).toBeTruthy();

  const issues = await request.get('/repos/github-username/test-repo-1/issues');

  expect(issues.ok()).toBeTruthy();
  expect(await issues.json()).toContainEqual(expect.objectContaining({
    title: '[Bug] report 1',
    body: 'Bug description',
  }));
});

In this example, we're using the post and get methods provided by the APIRequestContext class to send API requests and validate the server state.

The next step is to mock the routes themselves:

await page.route('https://example.com/api/data', (route) => {
  route.fulfill({
    status: 200,
    body: JSON.stringify({ message: 'Hello, World!' }),
  });
});

Whether the request originates from the APIRequestContext or the application, route will be able to intercept it.

Unfortunately, this means you cannot leverage nock API that you already familiar with, but it achieves the goal of mocking the API.

huangapple
  • 本文由 发表于 2023年5月25日 18:42:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76331415.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定