📋 目錄
單元測試驗證函式邏輯、整合測試驗證模組協作——但真正模擬用戶行為的,是 E2E(End-to-End)測試。Playwright 由 Microsoft 開發,支援三大瀏覽器(Chromium、Firefox、WebKit)、API 直覺、測試執行快速,成為 2024-2026 年最受歡迎的 E2E 測試框架。
前言:為什麼需要 E2E 測試?
單元測試再多,也無法告訴你:
- 用戶實際操作流程是否通順:從登入到結帳,整個 flow 是否真的走得完
- 跨頁面、跨系統的互動:多步驟表單、OAuth 登入、第三方 API 串接
- 與真實瀏覽器的相容性:某個 CSS 樣式在 Safari 上看起來是否正常
E2E 測試用真實的瀏覽器模擬用戶行為,是測試金字塔最頂層的驗證。
Playwright vs Cypress:核心差異
| 面向 | Cypress | Playwright |
|---|---|---|
| 支援瀏覽器 | 主要 Chrome/Electron | Chromium、Firefox、WebKit |
| 跨瀏覽器支援 | 弱 | 強(三大瀏覽器一次測試) |
| 測試平行執行 | 需要付費版 | 原生支援 |
| API 設計 | 直覺但封閉 | 直覺且完整(網路 interception、檔案上傳等) |
| 截圖 / 錄影 | 內建 | 內建 |
| 測試隔離 | 自動清理 | 手動或自動都行 |
| 生態 | 成熟但封閉 | 快速成長,工具鏈完整 |
Playwright 最大的技術優勢是 WebDriver 的替代方案:用 Chrome DevTools Protocol 直接控制瀏覽器,繞過傳統 WebDriver 的效能瓶頸,速度比 Cypress 快一個數量級。
Playwright 安裝與設定
安裝
npm init playwright@latest
# 或在現有專案安裝
npm install -D @playwright/test
npx playwright install --with-deps chromium
--with-deps 會一併安裝瀏覽器所需的系統依賴(Linux 需要)。
playwright.config.ts 基本設定
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests', // 測試檔案目錄
fullyParallel: true, // 平行執行所有測試
forbidOnly: !!process.env.CI, // CI 環境下不允許 only() 測試
retries: process.env.CI ? 2 : 0, // CI 失敗時重試
workers: process.env.CI ? 1 : undefined, // CI 平行度降低
reporter: [
['list'], // 命令列輸出
['html'], // HTML 報告
],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry', // 失敗時自動截圖與錄影
screenshot: 'only-on-failure',
},
projects: [
// 設定三個瀏覽器
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
第一個 Playwright 測試
測試登入流程
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
test.describe('登入功能', () => {
test('應該能夠用正確帳號密碼登入', async ({ page }) => {
// 前往登入頁
await page.goto('/login');
// 填入帳號密碼
await page.fill('[name="email"]', 'xiaoming@example.com');
await page.fill('[name="password"]', 'password123');
// 點擊登入按鈕
await page.click('button[type="submit"]');
// 等待跳轉到儀表板
await page.waitForURL('/dashboard');
// 驗證登入成功
await expect(page.getByText('歡迎回來')).toBeVisible();
});
test('應該在錯誤密碼時顯示錯誤訊息', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', 'xiaoming@example.com');
await page.fill('[name="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
// 等待錯誤訊息出現
await expect(page.getByRole('alert')).toHaveText('帳號或密碼錯誤');
});
});
Playwright 實用技巧
1. 等待元素出現
Playwright 預設會等待元素出現才操作,不需要手動加 waitFor:
// ✅ Playwright 會自動等這個元素出現,最多 30 秒(預設 timeout)
await page.click('#submit-button');
// ✅ 手動控制等待時間
await page.click('#modal', { timeout: 5000 });
// ✅ 等待 URL 變化
await page.waitForURL('/dashboard');
// ✅ 等待網路請求完成
await page.waitForResponse(
response => response.url().includes('/api/auth') && response.status() === 200
);
2. 網路請求 interception(Mock API)
不需要實際的後端,就能測試前端邏輯:
test('應該在 API 失敗時顯示錯誤提示', async ({ page }) => {
// Mock /api/users 回傳錯誤
await page.route('/api/users', route => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: '伺服器錯誤' }),
});
});
await page.goto('/users');
await expect(page.getByText('伺服器錯誤')).toBeVisible();
});
3. 跨瀏覽器截圖比對
test('登入頁在各瀏覽器看起來正確', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'password');
// 截圖會存在 test-results/ 目錄
await page.screenshot({ path: `login-form-${page.context().browser()?.browser()?.type()}.png` });
});
4. Playground:互動式除錯
// 在測試中加入 debug 行,Playwright 會停在這裡等你用 Playwright inspector 查看
test('debug example', async ({ page }) => {
await page.goto('/dashboard');
// 執行到這一行後停住,開啟 Playwright Inspector 視窗
await page.pause();
// ...
});
Playwright + GitHub Actions
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
e2e:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run E2E tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
常見問題
Q:Playwright 和 Cypress 哪個更適合我的專案?
A:選擇 Cypress 的理由——你只需要測試 Chrome/Electron,而且喜歡 Cypress 獨特的「同步」語法。選擇 Playwright 的理由——你需要跨瀏覽器支援(特別是 Safari/WebKit)、需要測試 API interception、或需要更完整的調試工具。新專案建議直接選 Playwright,技術債更少。
Q:Playwright 可以用來做 Visual Regression Testing 嗎?
A:可以。Playwright 支援 page.screenshot() 截圖功能,加上 pixelmatch 或 playwright-visual-regression 外掛可以做像素比對。這個方向可以參考「Visual Regression Testing」的文章。
Q:Playwright 的測試速度怎麼樣?
A:比 Cypress 快很多,特別是平行執行時。單一瀏覽器執行大約是 Cypress 的 2-3 倍快,多瀏覽器平行執行還會更快。但要注意 E2E 測試本身的特性——啟動瀏覽器、開頁面、等網路請求——這些時間是無法省掉的。
Q:測試要寫多少才夠?
A:E2E 測試的原則是測試核心 user journey,而不是追求覆蓋率。建議測試最多的是:
- 登入 / 註冊流程
- 付款流程(如果有的話)
- 關鍵的資料建立和刪除流程
其他周邊功能交給單元測試和整合測試就好。
總結:E2E 測試是測試金字塔的最後一塊
Playwright 的核心價值:用最少的設定,覆蓋最多的瀏覽器。
這篇文章涵蓋:
- Playwright 與 Cypress 的核心差異
- Playwright 安裝與 playwright.config.ts 設定
- 第一個登入流程測試
- 實用技巧(等待、API Mock、截圖)
- GitHub Actions 整合
下一步:在你的 React 或 Next.js 專案加入 Playwright,測試一個你最重要的 user journey。從「登入」開始,是最不容易失敗、最有價值的起點。