前一篇我們理解了 Playwright 的 async / await 機制如何讓測試能夠一步步按照順序執行,現在來談談 Playwright 另一個執行測試時很重要機制:「逾時」(Timeout),超過一定時間就不再等待,回傳錯誤並中止測試。因為 Playwright 有 auto-waiting 以及 auto-retry,因此必須運用「逾時」(Timeout)設定一個合理的等待時間以驗證結果,原因如下:
1. 避免無限等待
萬一某個元素或預期的事件遲遲不出現,或某些原因導致它根本不出現,如果沒有 Timeout,測試可能就永遠卡在這一步。
2. 防止資源浪費
在 CI/CD 中,設定 Timeout,以免浪費 Runner 的資源與時間,影響 pipeline 的執行。
3. 控制測試速度
我們預期進到某個頁面應該顯示某個元素,但畫面上可能根本就不會顯示,這時候不應該無限制等待,應該視為失敗,立刻結束測試並回傳錯誤。
我們在 Day 07 ~ Day 08 介紹內建 matcher 時與進階技巧時有提到,自動重試(Auto-retry) 的 matcher 具有「自動等待」(Auto-wating)的特性,預設為 5 秒,能夠加入參數,設定個別斷言的 Timeout 時間。除了 matcher 能夠設定 Timeout 之外,還有多個可配置的 Timeout 設定,讓我們能彈性設置以應對各種測試場景:
針對所有測試總和時間的限制,避免某些原因導致所有測試都錯誤時,測試時間過於冗長。
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// 設定 Global Timeout 為一小時
globalTimeout: 60 * 60 * 1000,
});
針對單一測試的設定,計時範圍涵蓋測試本身、fixtures 初始化與設定時間, 以及 hooks (如beforeEach
、beforeAll
、afterAll
) 的所有執行時間總和。
test('test', async ({ page }) => {
// 設定 Timeout 為 3 分鐘
test.setTimeout(3 * 60 * 1000);
// ...測試步驟
});
test('test', async ({ page }) => {
// Test Timeout 的三倍
test.slow();
// ...測試步驟
});
針對開啟網頁時設定 Timeout,避免網頁回應時間過長,造成資源浪費。
test('test', async ({ page }) => {
// 設定前往目標網址 Timeout 為 10 秒
await page.goto('/', { timeout: 10 * 1000 });
// ...測試步驟
});
可以為某個動作加上 Timeout,讓測試時間更加合理
test('test', async ({ page }) => {
// 設定點擊某個元素 Timeout 為 5 秒
await page.locator.click({ timeout: 5 * 1000 });
// 設定輸入內容 Timeout 為 5 秒
await page.locator.fill('cat', { timeout: 5 * 1000 });
// ...測試步驟
});
test('test', async ({ page }) => {
// ...測試步驟
// 在 matcher 設定 Timeout,即為這個斷言的個別 Timeout
await expect(page.locator).toBeVisible({ timeout: 10 * 1000 });
// ...測試步驟
});
以上 Timeout 都能在 playwright.config.ts
做全域設定:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// 設定每個測試 Timeout
timeout: 5 * 60 * 1000,
// 設定每個斷言 Timeout
expect: {
timeout: 10 * 1000,
},
use: {
// 設定前往目標網址 Timeout
navigationTimeout: 10 * 1000
// 設定每個動作 Timeout
actionTimeout: 5 * 1000,
},
// 此專案內每個測試 Timeout
projects: [
{
name: 'cat',
timeout: 60 * 1000,
},
});
以全域設定為基礎,如果全域沒有設定則以預設為基礎,再由 hooks 設定疊加 / 覆蓋(這部份在下一篇一併探討)或個別設定覆蓋,一般來說,local 設定 > global 設定,也就是離測試越靠近越優先:
到這裡,我們我們已經學會了如何掌控「時間」,懂得如何透過設定 Timeout 來應對各種場景,接下來,我們將認識 Hooks:before/describe/after 的設置,幫助我們建立更有結構、可複用性更高的測試腳本。