Skip to main content

模擬 API

簡介

Web API 通常以 HTTP 端點的方式實作。Playwright 提供 API 來模擬修改網路流量,包含 HTTP 和 HTTPS。頁面發出的任何請求,包括 XHRfetch 請求,都可以被追蹤、修改和模擬。您也可以使用包含頁面多個網路請求的 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 的呼叫,並將名為 'Loquat' 的新水果加入資料中。然後前往 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 Archive 檔案,包含頁面載入時所有網路請求的記錄。它包含請求和回應標頭、Cookie、內容、時間等資訊。您可以在測試中使用 HAR 檔案來模擬網路請求。您需要:

  1. 錄製 HAR 檔案。
  2. 將 HAR 檔案與測試一起提交。
  3. 在測試中使用已儲存的 HAR 檔案來路由請求。

錄製 HAR 檔案

要錄製 HAR 檔案,我們使用 Page.routeFromHAR()BrowserContext.routeFromHAR() 方法。此方法接受 HAR 檔案的路徑和可選的選項物件。選項物件可以包含 URL,這樣只有符合指定萬用字元模式的 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"

了解更多關於進階網路

模擬 WebSocket

下列程式碼會攔截 WebSocket 連線,並模擬整個 WebSocket 上的通訊,而不是連接到伺服器。此範例以 "response" 回應 "request"

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