AI 輔助 QA 工作流 2026 — Claude Code + Playwright + GitHub Actions 完整 SOP
「能不能用 AI 幫我寫測試?」這問題從 2023 年問到 2026 年,答案從「勉強能」變成「應該全用」。但 90% 的人沒得到該有的效益,因為工作流斷掉。
這篇給你一條從 spec → 測試 code → CI 部署的完整工作流,整合 Claude Code、Playwright、GitHub Actions,附實戰 prompt 範本、踩雷紀錄、失敗除錯流程。讀完就能落地。
為什麼 2026 是 AI 輔助 QA 的分水嶺
2024-2026 兩年最大的變化不是「AI 變強」,是 AI 進到開發流程內部。Claude Code 直接在 terminal 跑、Cursor 整合進 IDE、GitHub Copilot Workspaces 變成 task agent。
對 QA 來說,意味著:
| 工作項目 | 2023 純手寫 | 2026 AI 輔助 |
|---|---|---|
| 寫一隻 E2E case | 30-60 min | 5-10 min |
| 測試資料生成 | 15-30 min | 1-2 min |
| 失敗 case 除錯 | 20-40 min | 5-15 min |
| Page Object 建立 | 1-2 小時 | 10-20 min |
| Bug Report 撰寫 | 15 min | 2-3 min |
節省的時間應該拿去做: Exploratory testing、跨團隊溝通、設計測試策略、提升回歸率。不是拿去做更多 E2E case。
相關閱讀:AI 時代 QA 還有未來嗎 — 講 QA 職涯如何因應 AI 浪潮
工作流總覽
flowchart LR
A[PM Spec / Story] --> B[Claude Code<br>讀 spec + 產生 test plan]
B --> C[Claude Code<br>寫 Playwright test code]
C --> D[本地 Playwright<br>跑通 + 修 selector]
D --> E[git push]
E --> F[GitHub Actions<br>跑 e2e + 截圖]
F --> G{結果}
G -->|通過| H[Merge to main]
G -->|失敗| I[Claude Code 分析<br>失敗 log + 截圖]
I --> C
整個流程不超過 30 分鐘就能完成一個 user story 從 spec 到 CI 通過的測試。手寫至少 2 小時。
環境準備
1. 安裝 Claude Code
# 全域安裝(推薦)
curl -fsSL https://claude.ai/install.sh | sh
# 或用 npm
npm install -g @anthropic-ai/claude-code
# 登入(需要 Anthropic 帳號)
claude login
不熟 Claude Code 的話先看 Claude Code 完整使用指南
2. 初始化 Playwright 專案
mkdir my-e2e && cd my-e2e
npm init -y
npm install -D @playwright/test
npx playwright install chromium firefox webkit
3. 建立 CLAUDE.md(最關鍵)
在專案 root 建 CLAUDE.md,告訴 Claude 你的測試規範:
# 專案測試規範
## 測試框架
- Playwright + TypeScript
- 用 page.locator() 不用 page.$()
- 用 data-testid 為主 selector,css 為輔
## Page Object Model
- pages/ 目錄放 Page Object class
- tests/ 目錄放 spec
- fixtures/ 放共用 fixture
## 命名
- spec 檔: feature-name.spec.ts
- POM class: FeatureNamePage
## 不要做
- 不要用 sleep / waitForTimeout(除非註明理由)
- 不要 inline selector,一律封裝進 POM
- 不要硬編碼測試資料
這份 CLAUDE.md 是整個工作流的靈魂。沒有它,Claude 每次都得從零猜你的規範。
Step 1: Spec → Test Plan
PM 給你一份 spec,先讓 Claude 整理成 test plan。
Prompt 範本
Read the following spec and generate a structured test plan.
Spec:
[貼 spec 內容]
Output format:
1. Happy path scenarios (e.g., 用戶成功登入)
2. Edge cases (e.g., 密碼輸入錯誤、帳號鎖定)
3. Error handling
4. Negative scenarios (e.g., XSS 注入)
5. Data validation (e.g., email 格式)
For each scenario, output:
- ID (TC-001, TC-002...)
- Title (一句話描述)
- Preconditions
- Steps
- Expected result
- Priority (P0/P1/P2)
Claude 4.7 通常會生出 15-25 個 scenarios。挑 P0/P1 寫成測試 case,P2 列入手動驗證或 backlog。
實戰範例
Spec:登入頁面,輸入正確 email + password 後跳轉到 /dashboard。錯誤超過 5 次帳號鎖定 15 分鐘。
Claude 產生:
TC-001 P0: 正確帳密成功登入
TC-002 P0: email 格式錯誤顯示 inline error
TC-003 P0: 密碼錯誤顯示「密碼錯誤」訊息
TC-004 P1: 連續 5 次錯誤後帳號鎖定
TC-005 P1: 鎖定後 15 分鐘可重新嘗試
TC-006 P1: 鎖定期間正確密碼仍無法登入
TC-007 P2: XSS 注入嘗試(email 欄位)
TC-008 P2: SQL injection 嘗試(password 欄位)
TC-009 P2: 空 form submit 顯示 required 提示
TC-010 P2: 1 個瀏覽器多 tab 同時登入
Step 2: 寫 Page Object
Claude 第二步:產生對應頁面的 Page Object。
Prompt 範本
Generate a Playwright Page Object class for the login page.
Page HTML (relevant parts):
[貼 DOM 結構或 testid]
Requirements:
- TypeScript
- Use data-testid as primary selector
- Encapsulate: navigate(), fillEmail(), fillPassword(), clickSubmit(),
getErrorMessage(), isLockedOut()
- Add JSDoc comments
- Follow CLAUDE.md conventions
Claude 產出:
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
readonly lockoutMessage: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.getByTestId('login-email');
this.passwordInput = page.getByTestId('login-password');
this.submitButton = page.getByTestId('login-submit');
this.errorMessage = page.getByTestId('login-error');
this.lockoutMessage = page.getByTestId('login-lockout');
}
async navigate() {
await this.page.goto('/login');
}
async fillEmail(email: string) {
await this.emailInput.fill(email);
}
async fillPassword(password: string) {
await this.passwordInput.fill(password);
}
async clickSubmit() {
await this.submitButton.click();
}
async getErrorMessage(): Promise<string> {
return await this.errorMessage.textContent() ?? '';
}
async isLockedOut(): Promise<boolean> {
return await this.lockoutMessage.isVisible();
}
async login(email: string, password: string) {
await this.fillEmail(email);
await this.fillPassword(password);
await this.clickSubmit();
}
}
注意:Claude 預設用
getByTestId(),這是 Playwright 推薦的做法。如果你的網站沒裝 testid,先請工程師加,不要硬用text=或css=。詳見 Page Object Model
Step 3: 寫測試 spec
把 Page Object 跟測試案例丟給 Claude,產出 spec:
Prompt 範本
Generate Playwright spec for the login feature.
Test cases (from test plan):
TC-001 P0: 正確帳密成功登入
TC-002 P0: email 格式錯誤顯示 inline error
TC-003 P0: 密碼錯誤顯示「密碼錯誤」訊息
Page Object: LoginPage (已寫好)
Base URL: https://staging.example.com
Requirements:
- Use test.describe + test pattern
- Use fixtures for LoginPage instance
- Test data from fixtures/users.ts ([email protected] / Test1234)
- No inline selectors
- Add expect() with descriptive messages
Claude 產出:
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { validUser, invalidEmailFormat } from '../fixtures/users';
test.describe('Login Feature', () => {
let loginPage: LoginPage;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
await loginPage.navigate();
});
test('TC-001: 正確帳密成功登入後跳轉 dashboard', async ({ page }) => {
await loginPage.login(validUser.email, validUser.password);
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByTestId('user-menu')).toBeVisible();
});
test('TC-002: email 格式錯誤顯示 inline error', async () => {
await loginPage.fillEmail(invalidEmailFormat);
await loginPage.passwordInput.blur();
const error = await loginPage.getErrorMessage();
expect(error).toContain('email 格式錯誤');
});
test('TC-003: 密碼錯誤顯示提示訊息', async () => {
await loginPage.login(validUser.email, 'wrongpass');
const error = await loginPage.getErrorMessage();
expect(error).toContain('密碼錯誤');
});
});
Step 4: 本地跑通
npx playwright test --headed
Claude 生的測試 80% 第一次能跑,剩 20% 通常是:
- selector 跟實際 DOM 不符(請 Claude 重看 DOM)
- 時序問題(加 await expect(...).toBeVisible() 取代 waitForTimeout)
- testid 沒裝(請工程師補)
相關閱讀:Flaky Test 除錯指南
Step 5: GitHub Actions CI
把 .github/workflows/e2e.yml 也讓 Claude 寫:
name: E2E Tests
on:
pull_request:
branches: [main, develop]
schedule:
- cron: '0 2 * * *' # 每天 02:00 UTC 跑
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium firefox
- name: Run Playwright tests
run: npx playwright test --reporter=github
env:
BASE_URL: ${{ secrets.STAGING_URL }}
TEST_USER: ${{ secrets.TEST_USER }}
TEST_PASS: ${{ secrets.TEST_PASS }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
- uses: actions/upload-artifact@v4
if: failure()
with:
name: failure-screenshots
path: test-results/
retention-days: 7
Step 6: 失敗除錯流程
CI 失敗時的 SOP:
1. 下載 artifact
gh run download <run-id> --name failure-screenshots
gh run download <run-id> --name playwright-report
2. 把失敗 log 丟給 Claude
The following Playwright test failed in CI. Analyze the failure:
[貼 test code]
Error log:
[貼 error message + stack trace]
Screenshot description: [描述截圖看到什麼]
Possible causes:
1. ...
2. ...
Suggested fix:
Claude 4.7 對失敗分析準確率約 75%。常見幾類:
- selector 變了:UI 改版但 testid 沒同步
- 時序問題:API response 慢 → 用 expect().toBeVisible({ timeout: 10_000 })
- 資料污染:前一個 test 沒清乾淨 → 加 test.afterEach() cleanup
- 環境差異:CI 跑 headless 跟本地 headed 行為不同
3. 修完重跑
npx playwright test login.spec.ts --headed --debug
進階:Claude Code Agent Mode
Claude Code 2026 開始支援 Agent Mode,可以自動跑測試、看失敗、修 code、重跑循環,直到測試通過。
claude --agent "Run the e2e tests, fix any failures, and commit."
Agent 模式適合: - 大量機械性的失敗修正(如 selector 大規模重命名) - 跨多檔案的調整(如 API endpoint 整批改名)
不適合: - 邏輯複雜的測試案例設計 - 涉及商業決策的 trade-off
工作流 ROI 計算
跑 3 個月實際數據(QA 個人經驗):
| 項目 | 純手寫 | AI 輔助 | 節省 |
|---|---|---|---|
| 寫 1 隻 E2E case | 45 min | 8 min | 82% |
| 寫 Page Object | 1.5 hr | 12 min | 87% |
| 失敗除錯 | 30 min | 8 min | 73% |
| Test plan from spec | 1 hr | 5 min | 92% |
| 每週 saved | — | — | 約 12-15 hr |
省下的時間建議拿去: 1. 寫更扎實的 spec review(Spec Review checklist) 2. Exploratory testing(Exploratory Testing Playbook) 3. 規劃測試策略(Test Strategy vs Plan vs Case) 4. 學新工具 / 進修
常見地雷
1. 完全不寫 CLAUDE.md
Claude 每次都從零猜,產出風格不一致、難維護。第一週寫好 CLAUDE.md,省下後面 99% 重複功夫。
2. 不檢視 AI 產出直接 commit
Claude 偶爾會產出「看起來對但實際邏輯錯」的 code。Code review 仍是 QA 自己的責任。
3. 把所有 E2E 都讓 AI 寫
高商業價值的核心流程(如金流、登入、權限)建議手寫 + AI 校對,純機械性回歸再讓 AI 全包。
4. 沒有 Failure Loop
CI 失敗只看 log 不收集 artifact,下次失敗又從頭追。設好 artifact retention + Slack 通知,失敗 1 小時內處理。
5. 忽略測試報告品質
AI 寫 expect 訊息常太簡略。要求 expect(error).toContain('密碼錯誤', '當密碼錯誤時應顯示中文訊息') 這種自描述的 assertion,失敗時看 log 就知道哪裡壞。
一週導入計畫
| 天 | 動作 |
|---|---|
| Day 1 | 安裝 Claude Code + Playwright,建 CLAUDE.md |
| Day 2 | 挑 1 個 user story 試完整流程:spec → POM → spec |
| Day 3 | 跑本地通過,記錄踩雷與 Claude 弱點 |
| Day 4 | 設 GitHub Actions CI,跑通第一次 PR e2e |
| Day 5 | 把上週手寫的 5 個 case 全部讓 Claude 重寫一次比對 |
| Day 6 | 整理 prompt 範本,存進 prompts/ 目錄 |
| Day 7 | 內部分享,讓全組換上工作流 |
結語
AI 不會取代 QA,但用 AI 的 QA 會取代不用 AI 的 QA。
這個工作流不是一勞永逸,每 3-6 個月隨 Claude / Playwright 版本更新調整。重點是把「機械性重複工作」交給 AI,把「需要判斷的工作」留給人。
時間省下來不是用來寫更多 case,是用來提升整體測試品質與策略視角。這才是 QA 在 2026 該有的樣子。
相關閱讀: - AI 時代 QA 還有未來嗎 - Playwright Starter 完整入門 - GitHub Actions + Playwright 實戰 - Page Object Model 深度 - Flaky Test 除錯 - Claude Code 完整使用指南(9niche) - AI 聊天機器人比較(9niche)