API 測試與請求攔截
Playwright 不只能操作瀏覽器畫面,內建的 APIRequestContext 也能直接發送 HTTP 請求,適合測試後端 API,或在 UI 測試中攔截、模擬網路請求,兩者是完全不同但互補的用法。
純 API 測試
不需要開啟瀏覽器,直接對 API 發送請求並驗證回應,執行速度比操作畫面快很多:
import { test, expect } from '@playwright/test';
test('建立訂單 API 應回傳 201', async ({ request }) => {
const response = await request.post('/api/orders', {
data: { productId: 'sku-001', quantity: 2 },
});
expect(response.status()).toBe(201);
const body = await response.json();
expect(body).toMatchObject({ productId: 'sku-001', quantity: 2 });
});
request 是內建 Fixture,等同於一個 API Client;也可以在 playwright.config.ts 的 use.baseURL 設定共用的 API 網域,讓 API 測試與 UI 測試共用同一份設定。
結合 UI 測試:用 API 準備測試資料
實務上很常見的模式,是用 API 快速建立測試需要的前置資料,再切換到畫面驗證,避免每個測試都要重複走完整的 UI 操作流程:
test('已存在訂單可以在畫面上查到', async ({ page, request }) => {
const created = await request.post('/api/orders', {
data: { productId: 'sku-001', quantity: 1 },
});
const { id } = await created.json();
await page.goto(`/orders/${id}`);
await expect(page.getByText('sku-001')).toBeVisible();
});
攔截與模擬前端網路請求
page.route() 可以攔截頁面發出的網路請求,用來模擬後端還沒開發完成的 API、測試錯誤情境,或加速測試(略過真正的網路延遲):
test('API 回傳錯誤時應顯示錯誤訊息', async ({ page }) => {
await page.route('**/api/orders', (route) =>
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ message: '伺服器忙碌中' }),
})
);
await page.goto('/checkout');
await page.getByRole('button', { name: '送出訂單' }).click();
await expect(page.getByText('伺服器忙碌中')).toBeVisible();
});
route.fulfill() 直接回傳自訂內容,不會真正打到後端;也可以用 route.continue() 讓請求正常送出,但修改 Header 或參數,或用 route.abort() 模擬網路中斷、離線情境。這種手法讓「後端錯誤」「網路異常」這類原本難以穩定重現的情境,變得容易寫成測試。
API 測試 vs. UI 測試
| 面向 | API 測試 | UI 測試 |
|---|---|---|
| 執行速度 | 快,不需渲染畫面 | 較慢,需要瀏覽器渲染 |
| 驗證範圍 | 後端邏輯、資料正確性 | 使用者實際看到與操作的畫面 |
| 適合情境 | 準備測試資料、驗證 API 合約 | 驗證關鍵使用者流程、畫面互動 |
實務上建議依「測試金字塔」概念分配:大量的商業邏輯用 API 測試或單元測試快速覆蓋,畫面層級的 Playwright UI 測試則聚焦在少數關鍵使用者流程(如登入、結帳),避免所有情境都堆疊成又慢又多的 UI 測試。
下一步
接著看 身份驗證狀態與視覺比對,學習如何讓多個測試重複使用同一個登入狀態,以及用截圖比對偵測未預期的畫面變化。