> 九號工具站
返回列表

Visual Regression Testing — Percy / Chromatic / Playwright Snapshot 完整對比

Visual regression 測試完整指南。Pixel diff vs DOM diff、Playwright snapshot 免費版、Percy / Applitools / Chromatic 比較、AI 視覺比對、跨平台一致性、CI 整合。

QA visual-regression snapshot percy chromatic applitools
· 最後更新 2026-06-17 · 最後審閱 2026-08-30

💡 本文原刊於 qa.9niche.com,2026-08 併入 9niche.com 懶人包,內容照原文完整搬遷。

1. 前言

「UI 改 padding 改死人」、「按鈕從 red 變 dark-red 沒人發現」 — 這些功能性測試抓不到的、Visual Regression 抓得到。這篇給你完整工具地圖 + 實戰 setup。

2. 為什麼功能測試不夠

flowchart LR F[功能測試] -->|抓| F1["按鈕能點"] F -->|抓不到| F2["按鈕變小變色"] F -->|抓不到| F3["排版跑掉"] F -->|抓不到| F4["字體換錯"] F -->|抓不到| F5["圖片消失"] F -->|抓不到| F6["RWD 在 iPad 爆"] V[Visual Regression] -->|抓| V1[像素級變化] V -->|抓| V2[版面位移] V -->|抓| V3[顏色 / 字體] style F2 fill:#ef4444,color:#fff style F3 fill:#ef4444,color:#fff style F4 fill:#ef4444,color:#fff style V1 fill:#10b981,color:#fff

3. Visual Regression 工作流

flowchart LR PR[Dev push PR] --> Build[Build 新版] Build --> Snap[拍 N 張 screenshot] Snap --> Diff{對比 baseline} Diff -->|無變化| Pass[✓ Pass] Diff -->|有變化| Review[人類 review] Review -->|預期內| Accept[更新 baseline] Review -->|是 bug| Reject[退回 PR] style Pass fill:#10b981,color:#fff style Review fill:#a855f7,color:#fff style Reject fill:#ef4444,color:#fff

4. 工具對比

工具起跳價強項弱項
Playwright Snapshot免費內建、簡單沒 UI / approval workflow
Percy$39/月BrowserStack 整合、UI 強
Chromatic免費起跳Storybook 親兒子限 Storybook 友善
Applitools$1500/年AI 視覺、跨平台企業向、起價高
BackstopJS免費Open source、CLI自架、UX 普通
Loki免費Storybook 整合維護慢

5. Playwright Snapshot — 起步首選

基本用法

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

test('homepage looks right', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveScreenshot('homepage.png');
});

第一次跑 → 建立 baseline homepage.png。 之後跑 → 比對 baseline、有差 → fail。

配置(playwright.config.ts)

export default defineConfig({
  use: {
    // 關掉動畫避免 flaky
    actionTimeout: 0,
  },
  expect: {
    toHaveScreenshot: {
      // 允許 0.1% 像素差異(防 anti-aliasing flaky)
      maxDiffPixelRatio: 0.001,
      // 動畫 disabled
      animations: 'disabled',
      // 截圖時隱藏 cursor
      caret: 'hide',
    },
  },
});

進階:mask 動態元素

test('avoid time / random masking', async ({ page }) => {
  await page.goto('/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    mask: [
      page.locator('.timestamp'),
      page.locator('.random-banner'),
      page.locator('time'),
    ],
  });
});

跨裝置

test.describe('responsive', () => {
  for (const device of ['Desktop Chrome', 'iPhone 13', 'iPad Pro']) {
    test(`looks right on ${device}`, async ({ browser }) => {
      const context = await browser.newContext({ ...devices[device] });
      const page = await context.newPage();
      await page.goto('/');
      await expect(page).toHaveScreenshot(`home-${device}.png`);
    });
  }
});

更新 baseline

npx playwright test --update-snapshots

code review 時帶上 baseline diff 截圖、reviewer 才看得出變化是預期還是 bug。

6. Percy — 企業選擇

Setup

npm install --save-dev @percy/cli @percy/playwright
import { test } from '@playwright/test';
import percySnapshot from '@percy/playwright';

test('homepage', async ({ page }) => {
  await page.goto('https://example.com');
  await percySnapshot(page, 'homepage');
});

跑:

PERCY_TOKEN=xxx npx percy exec -- npx playwright test

Percy 強項

flowchart LR Pcy[Percy] --> S1[Web UI 看 diff] Pcy --> S2[一鍵 approve / reject] Pcy --> S3[並列前後對比] Pcy --> S4[Slack / GitHub 通知] Pcy --> S5[跨瀏覽器 baseline] Pcy --> S6[Responsive width 一次拍多寬] style Pcy fill:#a855f7,color:#fff

7. Chromatic — Storybook 神配

npm install --save-dev chromatic
npx chromatic --project-token=xxx

Chromatic 抓你所有 Storybook stories、自動拍 + 比對。Component 層級的視覺穩定 → 整個 UI 穩

優勢:

  • Component-level snapshot(粒度比 page 細)
  • 設計師 friendly UI
  • 跟 Figma 對齊

8. Applitools — AI 視覺比對

不是 pixel diff、是 「語意一致」比對:

  • 字體大小改 1px → AI 知道是同字體
  • 顏色 hex 改 1 號 → AI 知道是同顏色
  • 版面微調 → AI 容忍

減少 false positive 5-10 倍。但貴。

import { Eyes } from '@applitools/eyes-playwright';

test('with Applitools', async ({ page }) => {
  const eyes = new Eyes();
  await eyes.open(page, 'My App', 'Homepage Test');
  await page.goto('/');
  await eyes.check('Homepage');
  await eyes.close();
});

9. CI 整合範例

Playwright + GitHub Actions

name: Visual Tests
on: [pull_request]

jobs:
  visual:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --grep @visual
      - if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: visual-diff
          path: test-results/

PR 中失敗 → 下載 artifact 看 diff PNG。

10. 反 flaky 5 招

flowchart TD Flaky[Visual flaky 來源] --> F1[字體 anti-aliasing] Flaky --> F2[動畫] Flaky --> F3[時間 / 隨機資料] Flaky --> F4[網路 timing] Flaky --> F5[跨 OS 字體差] F1 --> S1[maxDiffPixelRatio: 0.001] F2 --> S2["animations: 'disabled'"] F3 --> S3[mask 動態元素] F4 --> S4[等狀態再截] F5 --> S5[同 docker / 同 OS 跑] style S1 fill:#10b981,color:#fff style S2 fill:#10b981,color:#fff style S3 fill:#10b981,color:#fff

11. 反模式

flowchart TD Anti[Visual 反模式] --> A1["拍整頁 → diff 太多沒人看"] Anti --> A2["不關動畫"] Anti --> A3["跨 OS 跑(字體差)"] Anti --> A4["每 PR 強制 0 diff(變 flaky 之源)"] Anti --> A5["沒 mask 時間 / 動態"] Anti --> A6["跑 prod data"] style A1 fill:#ef4444,color:#fff style A2 fill:#ef4444,color:#fff style A3 fill:#ef4444,color:#fff style A4 fill:#ef4444,color:#fff style A5 fill:#ef4444,color:#fff style A6 fill:#ef4444,color:#fff

12. 給 QA 的 5 句

  1. 拍小區塊比拍整頁穩
  2. 動態元素 mask、動畫 disable
  3. 同 docker 跑 = 跨平台一致性
  4. 新案先 Playwright snapshot、長大再 Percy
  5. Code review 時帶 diff 圖、reviewer 才能判斷

13. 最後

Visual Regression 是 「功能測試抓不到、但使用者一眼看出來」的最後防線。從 Playwright 內建 snapshot 起步、設好 threshold + mask、CI 自動跑、PR 帶 diff — 半年後 UI 退步 bug 砍 80%。

延伸:

ℹ️

一般聲明

本站提供之資訊僅供參考,不保證其完整性與正確性。使用者應自行判斷資訊之適用性。

意見反饋