模拟 API
介绍
Web API 通常以 HTTP 端点形式实现。Playwright 提供了 API 来模拟和修改网络流量,包括 HTTP 和 HTTPS。页面发出的任何请求,包括 XHRs 和 fetch 请求,都可以被跟踪、修改和模拟。使用 Playwright 还可以通过包含页面发出的多个网络请求的 HAR 文件来进行模拟。
模拟 API 请求
以下代码将拦截所有对 */**/api/v1/fruits
的调用,并返回自定义响应。不会向 API 发出实际请求。测试访问使用模拟路由的 URL,并断言页面上存在模拟数据。
test("模拟水果数据且不调用真实API", async ({ page }) => {
// 在导航前模拟 API 调用
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');
// 断言草莓水果可见
await expect(page.getByText('Strawberry')).toBeVisible();
});
从示例测试的跟踪记录中可以看到,API 从未被实际调用,而是用模拟数据进行了响应。
了解更多关于 高级网络功能。
修改 API 响应
有时我们需要发起 API 请求,但为了确保测试可重复性,必须对响应进行修改。这种情况下,与其完全模拟请求,不如实际发起请求并使用修改后的响应来完成。
以下示例中,我们拦截了对水果 API 的调用,并在数据中添加了一个名为"枇杷"的新水果。然后我们访问页面并断言该数据存在:
test('从API获取JSON并添加新水果', 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 });
// 使用原始响应完成请求,同时用给定的JSON对象修补响应体
await route.fulfill({ response, json });
});
// 访问页面
await page.goto('https://demo.playwright.dev/api-mocking');
// 断言新水果可见
await expect(page.getByText('Loquat', { exact: true })).toBeVisible();
});
在测试的追踪记录中,我们可以看到 API 被调用且响应被修改。
通过检查响应,可以看到新水果已被添加到列表中。
了解更多关于高级网络操作的内容。
使用 HAR 文件进行模拟
HAR 文件是一种 HTTP 存档文件,记录了页面加载时发起的所有网络请求。它包含请求和响应头信息、cookies、内容、时间等数据。您可以使用 HAR 文件在测试中模拟网络请求。具体需要以下步骤:
- 录制 HAR 文件
- 将 HAR 文件与测试代码一起提交
- 在测试中使用保存的 HAR 文件来路由请求
录制 HAR 文件
要录制 HAR 文件,我们使用 page.routeFromHAR() 或 browserContext.routeFromHAR() 方法。该方法接收 HAR 文件路径和一个可选配置对象。配置对象可以包含 URL 参数,这样只有匹配指定 glob 模式的请求才会从 HAR 文件中获取响应。如果不指定,所有请求都将从 HAR 文件中获取响应。
将 update
选项设为 true 时,会使用实际网络信息创建或更新 HAR 文件,而不是从 HAR 文件中提供响应。在创建测试时使用此选项,可以用真实数据填充 HAR 文件。
test('录制或更新 HAR 文件', async ({ page }) => {
// 从 HAR 文件获取响应
await page.routeFromHAR('./hars/fruit.har', {
url: '*/**/api/v1/fruits',
update: true,
});
// 访问页面
await page.goto('https://demo.playwright.dev/api-mocking');
// 断言水果可见
await expect(page.getByText('Strawberry')).toBeVisible();
});
修改 HAR 文件
录制完成 HAR 文件后,您可以通过编辑 'hars' 文件夹中带哈希值的 .txt 文件来修改其中的 JSON 内容。该文件应当提交到您的源代码管理中。当您使用 update: true
参数运行此测试时,系统会用 API 的最新请求更新您的 HAR 文件。
[
{
"name": "Playwright",
"id": 100
},
// ... 其他水果数据
]
从 HAR 文件回放
现在你已经录制好 HAR 文件并修改了模拟数据,可以在测试中使用它来提供匹配的响应。为此,只需关闭或直接移除 update
选项。这样测试就会针对 HAR 文件运行,而不会实际调用 API。
test('从HAR获取json并检查新水果是否已添加', async ({ page }) => {
// 从HAR回放API请求
// 要么使用HAR中的匹配响应
// 如果没有匹配项则中止请求
await page.routeFromHAR('./hars/fruit.har', {
url: '*/**/api/v1/fruits',
update: false,
});
// 访问页面
await page.goto('https://demo.playwright.dev/api-mocking');
// 断言Playwright水果可见
await expect(page.getByText('Playwright', { exact: true })).toBeVisible();
});
在测试的追踪记录中,我们可以看到路由是通过 HAR 文件完成的,并没有实际调用 API。
如果我们检查响应,可以看到新水果已被添加到 JSON 中,这是通过手动更新 hars
文件夹中哈希的 .txt
文件实现的。
HAR 回放会严格匹配 URL 和 HTTP 方法。对于 POST 请求,还会严格匹配 POST 负载。如果有多个录制匹配请求,则会选择匹配头信息最多的条目。导致重定向的条目会被自动跟随。
与录制时类似,如果给定的 HAR 文件名以 .zip
结尾,则会被视为包含 HAR 文件和网络负载(存储为单独条目)的归档文件。你也可以解压这个归档,手动编辑负载或 HAR 日志,然后指向解压后的 har 文件。所有负载都将相对于文件系统中解压后的 har 文件路径进行解析。
使用 CLI 录制 HAR 文件
我们推荐使用 update
选项来为测试录制 HAR 文件。不过,你也可以通过 Playwright CLI 来录制 HAR。
使用 Playwright CLI 打开浏览器并传递 --save-har
选项来生成 HAR 文件。可选地,使用 --save-har-glob
只保存你感兴趣的请求,例如 API 端点。如果 har 文件名以 .zip
结尾,所有资源会被写入单独的文件并压缩为一个 zip 包。
# 将 example.com 的 API 请求保存为 "example.har" 归档文件
npx playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.com
了解更多关于 高级网络功能 的信息。
模拟 WebSocket
以下代码将拦截 WebSocket 连接并模拟整个 WebSocket 通信,而不是连接到实际服务器。这个示例会响应 "request"
消息并返回 "response"
。
await page.routeWebSocket('wss://example.com/ws', ws => {
ws.onMessage(message => {
if (message === 'request')
ws.send('response');
});
});
或者,你可能希望连接到真实服务器,但在消息传递过程中拦截并修改或阻止它们。下面是一个示例,它会修改页面发送给服务器的部分消息,其余消息保持不变。
await page.routeWebSocket('wss://example.com/ws', ws => {
const server = ws.connectToServer();
ws.onMessage(message => {
if (message === 'request')
server.send('request2');
else
server.send(message);
});
});
更多细节请参阅 WebSocketRoute。