Playwright 入門 — 從 0 到第一個能跑的 E2E(30 分鐘上手)

如果你已經會寫手動 test case,學 Playwright 就是把同樣的步驟用 code 寫出來。難點不在 API,在「怎麼選元素」跟「怎麼等對的東西」。

為什麼選 Playwright

對手 弱點
Selenium API 老派、wait 很麻煩、要自己裝 driver
Cypress 只支援 Chromium、沒 multi-tab、跨 origin 麻煩
Puppeteer 純 Chromium、沒測試 runner

Playwright 強項:原生支援 Chromium/Firefox/WebKit、auto-wait、trace viewer 神器、TypeScript 一等公民。新案直接選它。

30 分鐘從 0 到 1

1. 安裝(2 分鐘)

npm init -y
npm init playwright@latest

CLI 會問你:

  • TypeScript or JavaScript?→ TypeScript(型別檢查抓 bug)
  • 測試放哪?→ 預設 tests/
  • GitHub Actions workflow?→ Yes(之後就有 CI 模板)
  • Install browsers?→ Yes

裝完看到 tests/example.spec.tsplaywright.config.ts 就成功。

2. 跑第一個範例(1 分鐘)

npx playwright test

跑完開報告:

npx playwright show-report

3. 寫自己的測試(10 分鐘)

新增 tests/login.spec.ts

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

test('登入失敗顯示錯誤訊息', async ({ page }) => {
  await page.goto('https://yourapp.com/login');

  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Password').fill('wrong-password');
  await page.getByRole('button', { name: '登入' }).click();

  await expect(page.getByText('帳號或密碼錯誤')).toBeVisible();
});

跑:

npx playwright test login.spec.ts

恭喜,第一支跑起來了。

Selector 怎麼選(最關鍵)

新手最常犯的錯:用 CSS selector .btn-primary。class 改了測試就壞。

優先順序:

  1. getByRole — 最穩,跟著無障礙樹走,UI 怎麼改都對 typescript page.getByRole('button', { name: '送出' })
  2. getByLabel — 表單欄位首選 typescript page.getByLabel('使用者名稱')
  3. getByText — 純文字內容 typescript page.getByText('購物車(3)')
  4. getByTestId — 上面都不行才用,且要跟前端團隊約好 data-testid 規範 typescript page.getByTestId('checkout-button')

禁忌page.locator('.css-1a2b3c')(CSS-in-JS 雜湊每次 build 都變)、page.locator('div > div:nth-child(3) > button')(DOM 微調就壞)。

Auto-wait — 不用自己寫 sleep

// ❌ 舊習慣
await page.waitForTimeout(2000);
await page.click('.button');

// ✅ Playwright 寫法
await page.getByRole('button', { name: '送出' }).click();
// Playwright 自動等到元素:可見、enabled、stable、能被點擊

expect() 也自動 retry:

await expect(page.getByText('訂單已建立')).toBeVisible({ timeout: 10000 });
// 預設 5 秒內每 100ms retry 一次

結論:忘掉 sleep,用 expect.toBeVisible() 等狀態。

Debug 三神器

  1. --headed — 看著瀏覽器跑 bash npx playwright test --headed --workers=1
  2. --debug — Playwright Inspector,逐步執行 bash npx playwright test login.spec.ts --debug
  3. Trace Viewer — fail 後重現 bash # playwright.config.ts 設定 trace: 'on-first-retry' npx playwright show-trace trace.zip Trace viewer 會錄下每步的 DOM snapshot、network、console,比影片有用十倍。

CI 整合(GitHub Actions)

init 已幫你生 .github/workflows/playwright.yml

name: Playwright Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7

PR 提交就跑、fail 上傳 report、點下載就能 debug。

新手避雷

  1. 不要每個 test 都 goto — 用 beforeEachstorageState 重用登入。
  2. 不要測 third-party 細節 — 別把 Google OAuth 內頁也寫進去,會被改死。
  3. selector 用 page object 包起來 — 5 個檔案以上就抽 LoginPage class,selector 改一處就好。
  4. flaky test 不要忽略 — 通常是 race condition,加 retry 只是蓋住問題。
  5. 跨瀏覽器先別開全 — 起步先 Chromium,等穩定再加 Firefox/WebKit。

下一步

  • fixtures 重用登入狀態
  • expect(page).toHaveScreenshot() 做視覺迴歸
  • request API 直接打 API 不開瀏覽器(測 API + UI 混合)

學會選 selector + 用 auto-wait + 看懂 trace,你就會用 Playwright 了。剩下都是熟練度。