跳转到主要内容

Mock API

简介

Web APIs 通常实现为 HTTP 端点。Playwright 提供了用于 模拟 (mock)修改 (modify) 网络流量(包括 HTTP 和 HTTPS)的 API。页面发出的任何请求,包括 XHRsfetch 请求,都可以被跟踪、修改和模拟。使用 Playwright,您还可以使用包含页面发出的多个网络请求的 HAR 文件进行模拟。

模拟 API 请求

以下代码将拦截所有对 */**/api/v1/fruits 的调用,并返回一个自定义响应。不会发出对该 API 的实际请求。测试将访问使用模拟路由的 URL,并断言页面上存在模拟数据。

// Intercept the route to the fruit API
page.route("https://fruit.ceo/api/breeds/image/random", route -> {
List<Dictionary<String, Object>> data = new ArrayList<Dictionary<String, Object>>();
Hashtable<String, Object> dict = new Hashtable<String, Object>();
dict.put("name", "Strawberry");
dict.put("id", 21);
data.add(dict);
// fulfill the route with the mock data
route.fulfill(RequestOptions.create().setData(data));
});

// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");

// Assert that the Strawberry fruit is visible
assertThat(page.getByText("Strawberry")).isVisible();

您可以从示例测试的跟踪中看到,API 从未被调用,但它确实是用模拟数据满足的。 api mocking trace

了解更多关于高级网络的内容。

修改 API 响应

有时,发出 API 请求是必不可少的,但响应需要修补以实现可重现的测试。在这种情况下,可以执行请求并用修改后的响应来满足它,而不是模拟请求。

在下面的示例中,我们拦截了对水果 API 的调用,并向数据中添加了一种名为“枇杷”的新水果。然后我们转到 URL 并断言该数据存在。

page.route("*/**/api/v1/fruits", route -> {
Response response = route.fetch();
byte[] json = response.body();
JsonObject parsed = new Gson().fromJson(new String(json), JsonObject.class);
parsed.add(new JsonObject().add("name", "Loquat").add("id", 100));
// Fulfill using the original response, while patching the response body
// with the given JSON object.
route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(parsed.toString()));
});

// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");

// Assert that the Loquat fruit is visible
assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible();

在我们的测试跟踪中,我们可以看到 API 被调用了,并且响应被修改了。 trace of test showing api being called and fulfilled

通过检查响应,我们可以看到我们的新水果已添加到列表中。 trace of test showing the mock response

了解更多关于高级网络的内容。

使用 HAR 文件进行模拟

HAR 文件是 HTTP 档案 (HTTP Archive) 文件,其中包含页面加载时发出的所有网络请求的记录。它包含有关请求和响应头、cookie、内容、计时等信息。您可以使用 HAR 文件来模拟测试中的网络请求。您需要

  1. 记录 HAR 文件。
  2. 将 HAR 文件与测试一起提交。
  3. 在测试中使用保存的 HAR 文件路由请求。

记录 HAR 文件

要录制 HAR 文件,我们使用 Page.routeFromHAR()BrowserContext.routeFromHAR() 方法。此方法接受 HAR 文件的路径和一个可选的选项对象。选项对象可以包含 URL,以便只有与指定 glob 模式匹配的 URL 的请求才从 HAR 文件中提供服务。如果未指定,所有请求都将从 HAR 文件中提供服务。

update 选项设置为 true 将创建或更新 HAR 文件,其中包含实际网络信息,而不是从 HAR 文件提供请求。在创建测试以使用真实数据填充 HAR 时使用它。

另外,您也可以在创建浏览器上下文时,通过在 Browser.newContext() 中使用 setRecordHarPath 选项来录制 HAR 文件。这允许您捕获整个上下文的所有网络流量,直到上下文关闭。

// Get the response from the HAR file
page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions()
.setUrl("*/**/api/v1/fruits")
.setUpdate(true)
);

// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");

// Assert that the fruit is visible
assertThat(page.getByText("Strawberry")).isVisible();

修改 HAR 文件

记录 HAR 文件后,您可以通过打开 'hars' 文件夹内的哈希 .txt 文件并编辑 JSON 来修改它。此文件应提交到您的源代码管理。每当您使用 update: true 运行此测试时,它都会使用来自 API 的请求来更新您的 HAR 文件。

[
{
"name": "Playwright",
"id": 100
},
// ... other fruits
]

从 HAR 重播

现在您已经记录了 HAR 文件并修改了模拟数据,可以在测试中使用它来提供匹配的响应。为此,只需关闭或简单地删除 update 选项。这将使测试针对 HAR 文件运行,而不是击中 API。

// Replay API requests from HAR.
// Either use a matching response from the HAR,
// or abort the request if nothing matches.
page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions()
.setUrl("*/**/api/v1/fruits")
.setUpdate(false)
);

// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");

// Assert that the Playwright fruit is visible
assertThat(page.getByText("Playwright", new Page.GetByTextOptions()
.setExact(true))).isVisible();

在我们的测试跟踪中,我们可以看到路由是从 HAR 文件满足的,并且没有调用 API。 trace showing the HAR file being used

如果我们检查响应,我们可以看到我们的新水果已添加到 JSON 中,这是通过手动更新 hars 文件夹内哈希的 .txt 文件完成的。 trace showing response from HAR file

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

# Save API requests from example.com as "example.har" archive.
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="open --save-har=example.har --save-har-glob='**/api/**' https://example.com"

了解更多关于高级网络的内容。

模拟 WebSockets

以下代码将拦截 WebSocket 连接并模拟整个 WebSocket 通信,而不是连接到服务器。此示例响应 "request" 并带有一个 "response"

page.routeWebSocket("wss://example.com/ws", ws -> {
ws.onMessage(frame -> {
if ("request".equals(frame.text()))
ws.send("response");
});
});

或者,您可能希望连接到实际服务器,但在中间拦截消息并修改或阻止它们。这是一个修改页面发送给服务器的一些消息,并保持其余消息不变的示例。

page.routeWebSocket("wss://example.com/ws", ws -> {
WebSocketRoute server = ws.connectToServer();
ws.onMessage(frame -> {
if ("request".equals(frame.text()))
server.send("request2");
else
server.send(frame.text());
});
});

有关更多详细信息,请参阅 WebSocketRoute