從端到端測試開始:用 Playwright 守護前端品質的實戰指南

從端到端測試開始:用 Playwright 守護前端品質的實戰指南

如果你已經用 Vitest 替元件寫了單元測試,恭喜你,已經守住了「函數」與「元件」這一層。但老實說,使用者從來不在乎你的某個函數有沒有通過測試——他們只在乎打開瀏覽器後,能不能順利登入、填完表單、看到正確的結果。很多時候單元測試全綠,使用者卻在真實頁面裡卡關:按鈕點了沒反應、表單跳轉錯頁、手機版排版整個崩掉。這,就是端到端測試(End-to-End, E2E)要解決的戰場。

一、什麼是端到端測試?

端到端測試模擬真實使用者,從頭到尾跑完一條業務流程:開瀏覽器 → 輸入網址 → 登入 → 操作 → 斷言結果。它不關心內部實作,只在乎「使用者看到的行為對不對」。相對於單元測試(測一個函數)、整合測試(測模組協作),E2E 是離使用者最近、也最貼近真實的一層防護網。

二、為什麼選 Playwright?

過去談 E2E,大家直覺想到 Selenium,但它笨重、容易因為「元素還沒出現」而閃爍(flaky)。後來的 Cypress 體驗好很多,卻只支援 Chromium 系瀏覽器、且對多分頁與登入態切換較為不便。Playwright 由微軟推出,原生支援 Chromium、Firefox、WebKit(Safari 引擎)三大引擎,自帶自動等待(auto-wait),幾乎不用寫 sleep,穩定性明顯更好,這也是它近兩年快速竄紅的原因。

三、環境準備與安裝

步驟 1:在專案中安裝 Playwright(以 Node.js 前端專案為例):

npm init playwright@latest
# 或手動安裝
npm install -D @playwright/test
npx playwright install   # 下載瀏覽器驅動

步驟 2:確認專案根目錄產生了 playwright.config.ts 與 tests/ 資料夾。

步驟 3:在 playwright.config.ts 中設定 baseURL 與要跑的瀏覽器:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL: 'http://localhost:5173', // 你本地開發伺服器
    headless: true,
  },
  projects: [
    { name: 'chromium', use: { browserName: 'chromium' } },
    { name: 'firefox', use: { browserName: 'firefox' } },
    { name: 'webkit', use: { browserName: 'webkit' } },
  ],
});

四、寫出第一個登入測試

步驟 1:在 tests/login.spec.ts 寫下模擬登入的流程。

import { test, expect } from '@playwright/test';

test('使用者可以成功登入並看到儀表板', async ({ page }) => {
  await page.goto('/login');

  // 填寫表單
  await page.fill('input[name="username"]', 'demo');
  await page.fill('input[name="password"]', 'secret');
  await page.click('button[type="submit"]');

  // 斷言:登入後跳轉到儀表板
  await expect(page).toHaveURL(/\/dashboard/);
  await expect(page.getByText('歡迎回來')).toBeVisible();
});

步驟 2:執行測試:

npx playwright test

重點:Playwright 會自動等待元素可點擊、可見,再執行動作,因此你幾乎不必手寫 waitFor 或 sleep,這是它比老工具穩定的關鍵。

五、進階技巧:選擇器、等待與夾具

步驟 1:優先使用以角色或文字為基礎的選擇器(getByRole、getByText、getByLabel),它們對 CSS 重構更魯棒,不會因為 class 改名而全軍覆沒。

await page.getByRole('button', { name: '送出' }).click();
await page.getByLabel('電子郵件').fill('a@b.com');

步驟 2:用 fixtures 抽離重複動作,例如把「登入」封裝成可復用的步驟,讓多個測試共用,減少維護成本。

步驟 3:用 expect 的軟斷言(soft assert)或 test.step 把流程分層,報錯時能一眼定位是哪一關卡住。

六、在 CI 中跑起來

重點:E2E 測試的最大價值,是在每次合併前自動擋下會破壞流程的程式碼。把 Playwright 接進 CI(如 GitHub Actions):

name: e2e
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run build
      - run: npx playwright test

搭配 --reporter=html 產出可視化報表,失敗時還能拿到自動截圖與錄影,排查問題事半功倍。

E2E 測試不是要取代單元測試,而是補上「使用者視角」這最後一塊拼圖。單元測試回答「程式對不對」,E2E 回答「產品能不能用」。兩者搭配,你的前端品質才真正站得住腳。

互動話題:你們團隊目前有在跑端到端測試嗎?最常被「閃爍測試(flaky test)」困擾的場景是什麼?歡迎在留言區分享你的踩坑經驗!