---
title: Playwright 入門 — 從 0 到第一個能跑的 E2E（30 分鐘上手）
description: Playwright 安裝、第一個測試、Selectors 選擇策略、debug 技巧、CI 整合。給有測試經驗但沒寫過自動化的 QA。
category: automation
tags: [playwright, e2e, 自動化, 入門]
date: 2026-06-09
---

# 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 分鐘）

```bash
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.ts` 跟 `playwright.config.ts` 就成功。

### 2. 跑第一個範例（1 分鐘）

```bash
npx playwright test
```

跑完開報告：

```bash
npx playwright show-report
```

### 3. 寫自己的測試（10 分鐘）

新增 `tests/login.spec.ts`：

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

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

  await page.getByLabel('Email').fill('test@example.com');
  await page.getByLabel('Password').fill('wrong-password');
  await page.getByRole('button', { name: '登入' }).click();

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

跑：

```bash
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

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

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

`expect()` 也自動 retry：

```typescript
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`：

```yaml
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`** — 用 `beforeEach` 或 `storageState` 重用登入。
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 了。剩下都是熟練度。
