模擬 API
簡介
Web API 通常以 HTTP 端點實作。Playwright 提供 API 來模擬和修改網路流量,包括 HTTP 和 HTTPS。頁面執行的任何請求,包含 XHR 和 fetch 請求,都可以被追蹤、修改和模擬。透過 Playwright,您也可以使用 HAR 檔案進行模擬,HAR 檔案包含頁面發出的多個網路請求。
模擬 API 請求
以下程式碼將攔截所有對 */**/api/v1/fruits
的呼叫,並改為傳回自訂回應。將不會對 API 發出任何請求。測試會前往使用模擬路由的 URL,並斷言頁面上存在模擬資料。
// Intercept the route to the fruit API
await page.RouteAsync("*/**/api/v1/fruits", async route => {
var json = new[] { new { name = "Strawberry", id = 21 } };
// fulfill the route with the mock data
await route.FulfillAsync(new()
{
Json = json
});
});
// Go to the page
await page.GotoAsync("https://demo.playwright.dev/api-mocking");
// Assert that the Strawberry fruit is visible
await Expect(page.GetByTextAsync("Strawberry")).ToBeVisibleAsync();
您可以從範例測試的追蹤中看到,API 從未被呼叫,但已使用模擬資料完成。
閱讀更多關於進階網路功能的資訊。
修改 API 回應
有時,執行 API 請求至關重要,但需要修補回應以允許可重現的測試。在這種情況下,可以執行請求並使用修改後的回應來完成請求,而不是模擬請求。
在以下範例中,我們攔截對水果 API 的呼叫,並將一個名為 'Loquat' 的新水果添加到資料中。然後我們前往該 URL 並斷言該資料存在。
await page.RouteAsync("*/**/api/v1/fruits", async (route) => {
var response = await route.FetchAsync();
var fruits = await response.JsonAsync<Fruit[]>();
fruits.Add(new Fruit() { Name = "Loquat", Id = 100 });
// Fulfill using the original response, while patching the response body
// with the given JSON object.
await route.FulfillAsync(new ()
{
Response = response,
Json = fruits
});
}
);
// Go to the page
await page.GotoAsync("https://demo.playwright.dev/api-mocking");
// Assert that the Loquat fruit is visible
await Expect(page.GetByTextAsync("Loquat", new () { Exact = true })).ToBeVisibleAsync();
在我們測試的追蹤中,我們可以看到 API 已被呼叫,並且回應已被修改。
透過檢查回應,我們可以看見我們的新水果已新增至清單中。
閱讀更多關於進階網路功能的資訊。
使用 HAR 檔案進行模擬
HAR 檔案是一種 HTTP Archive 檔案,其中包含頁面載入時所發出所有網路請求的記錄。它包含關於請求和回應標頭、Cookie、內容、時間等的資訊。您可以使用 HAR 檔案在測試中模擬網路請求。您需要
- 錄製 HAR 檔案。
- 將 HAR 檔案與測試一起提交。
- 在測試中使用已儲存的 HAR 檔案路由請求。
錄製 HAR 檔案
為了錄製 HAR 檔案,我們使用 Page.RouteFromHARAsync() 或 BrowserContext.RouteFromHARAsync() 方法。此方法接受 HAR 檔案的路徑和可選的選項物件。選項物件可以包含 URL,以便只有符合指定 glob 模式的 URL 的請求才會從 HAR 檔案提供。如果未指定,所有請求都將從 HAR 檔案提供。
將 update
選項設定為 true 將會建立或更新 HAR 檔案,其中包含實際的網路資訊,而不是從 HAR 檔案提供請求。在建立測試以使用真實資料填入 HAR 時使用它。
// Get the response from the HAR file
await page.RouteFromHARAsync("./hars/fruit.har", new () {
Url = "*/**/api/v1/fruits",
Update = true,
});
// Go to the page
await page.GotoAsync("https://demo.playwright.dev/api-mocking");
// Assert that the fruit is visible
await Expect(page.GetByText("Strawberry")).ToBeVisibleAsync();
修改 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.
await page.RouteFromHARAsync("./hars/fruit.har", new ()
{
Url = "*/**/api/v1/fruits",
Update = false,
}
);
// Go to the page
await page.GotoAsync("https://demo.playwright.dev/api-mocking");
// Assert that the Playwright fruit is visible
await page.ExpectByTextAsync("Playwright", new() { Exact = true }).ToBeVisibleAsync();
在我們測試的追蹤中,我們可以看到路由已從 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
。
# Save API requests from example.com as "example.har" archive.
pwsh bin/Debug/netX/playwright.ps1 open --save-har=example.har --save-har-glob="**/api/**" https://example.com
閱讀更多關於進階網路功能的資訊。
模擬 WebSocket
以下程式碼將攔截 WebSocket 連線並模擬透過 WebSocket 的整個通訊,而不是連線到伺服器。此範例會以 "response"
回應 "request"
。
await page.RouteWebSocketAsync("wss://example.com/ws", ws => {
ws.OnMessage(frame => {
if (frame.Text == "request")
ws.Send("response");
});
});
或者,您可能想要連線到實際的伺服器,但在之間攔截訊息並修改或封鎖它們。以下範例修改了頁面傳送到伺服器的某些訊息,並保持其餘訊息不修改。
await page.RouteWebSocketAsync("wss://example.com/ws", ws => {
var server = ws.ConnectToServer();
ws.OnMessage(frame => {
if (frame.Text == "request")
server.Send("request2");
else
server.Send(frame.Text);
});
});
如需更多詳細資訊,請參閱 WebSocketRoute。