4.3 處理網路與 API:攔截與 Mock#
為什麼要攔截網路?#
E2E 測試依賴真實後端時:
- 慢:每次測試都打真實 API、真實資料庫
- 不穩:後端資料變了,測試就跟著壞
- 測不了極端情況:怎麼手動觸發「伺服器 500」?
解法:攔截(route)網路請求,回傳假資料(Mock)。
攔截請求:回傳假資料#
test("商品清單應該正確顯示", async ({ page }) => {
// 攔截 /api/products,回傳假資料
await page.route("**/api/products", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([
{ id: 1, name: "蘋果", price: 30 },
{ id: 2, name: "香蕉", price: 15 },
]),
});
});
await page.goto("https://my-site.com/products");
await expect(page.getByText("蘋果")).toBeVisible();
await expect(page.getByText("香蕉")).toBeVisible();
});
page.route(模式, 處理函式):符合模式的請求會被攔截**是萬用字元,**/api/products符合任何網域下的這個路徑route.fulfill(...):直接回傳假回應,請求不會真的發出
模擬錯誤情境#
Mock 的最大價值:輕鬆觸發手動做不出的情況:
// 模擬伺服器錯誤
await page.route("**/api/products", async (route) => {
await route.fulfill({ status: 500 });
});
// 驗證錯誤處理 UI
await page.goto("https://my-site.com/products");
await expect(page.getByText("載入失敗,請稍後再試")).toBeVisible();
// 模擬網路斷線
await page.route("**/api/**", route => route.abort());
修改真實回應#
不回假資料,而是攔截後改一改再放行:
await page.route("**/api/user", async (route) => {
const response = await route.fetch(); // 放行到真實後端
const json = await response.json();
json.name = "測試使用者"; // 修改回應
await route.fulfill({ response, json });
});
用 API 準備測試資料(比 UI 操作快)#
登入、建立測試資料等前置工作,用 request fixture 直接打 API,比透過 UI 操作快十倍:
test("已登入使用者可以看到訂單", async ({ page, request }) => {
// 用 API 登入(繞過 UI)
const loginRes = await request.post("https://my-site.com/api/login", {
data: { email: "user@test.com", password: "password123" },
});
const { token } = await loginRes.json();
// 把 token 塞進 localStorage,讓頁面認為已登入
await page.addInitScript((t) => {
localStorage.setItem("token", t);
}, token);
await page.goto("https://my-site.com/orders");
await expect(page.getByText("我的訂單")).toBeVisible();
});
心法:UI 操作只留給「被測試的行為」本身;前置準備(登入、造資料)走 API,極端情況(500、斷線)用 route mock。快又穩。