使用 Playwright 做 Screenshot Test:改 Legacy 圖表時,至少知道畫面有沒有壞掉

2026/06/23

分享如何用 Playwright 與 pixelmatch 建立 E2E Screenshot Test,比對新舊環境的圖表畫面,降低 Legacy API 替換時的回歸風險。
Abstract testing flow banner

最近在處理一個舊專案的 API 替換。

這個專案的資料流不算乾淨。
頁面上有很多圖表、很多指標、很多股票,資料會經過一層又一層轉換,最後才變成畫面上的線圖或長條圖。

所以當後端說:

新 API 的格式會跟以前一樣。

我第一個反應其實沒有比較放心,反而是:

那我要怎麼確認真的一樣?

在 Legacy 專案裡,最可怕的通常不是整個畫面爆掉。
整個爆掉很好發現。

麻煩的是這種:

  • 圖表有出現,但數值怪怪的
  • X 軸年份少一個
  • 某支股票正常,另一支股票壞掉
  • 本機看起來可以,正式站看起來不一樣
  • 差異很小,上線後才被發現

這種問題很難只靠人工慢慢點。
所以我做了一個 E2E Screenshot Test,用來比較:

環境代表
Local新 API
Production舊 API

簡單講,就是讓 Playwright 同時打開兩邊,登入、進同一頁、切同一支股票,然後把同一張圖表截下來比對。

我想解決的問題

這個測試不負責證明資料一定正確。

它主要想回答一個比較務實的問題:

我把 API 換掉之後,畫面有沒有跟原本不一樣?

這對圖表類功能很重要。

一般 E2E test 可以檢查:

ts
await expect(page.getByText('每股盈餘預測')).toBeVisible();

但這只能證明「圖表標題有出現」。
它沒辦法告訴我:

  • 線有沒有畫錯
  • 長條圖高度有沒有不同
  • X 軸年份有沒有少
  • 預估資料有沒有被吃掉
  • Local 跟 Production 看起來是不是一致

所以我選擇直接截圖,比像素差異。

大致流程

整個測試流程其實不複雜:

txt
開 Local 頁面開 Production 頁面  ↓兩邊都登入  ↓進到同一個 Dashboard  ↓切換同一支股票  ↓等待資料載入  ↓把頁面往下捲,讓圖表都載入  ↓找到指定圖表  ↓兩邊各截一張圖  ↓用 pixelmatch 比對  ↓輸出 local / prod / diff 圖片和報告

我把它包成一個 factory function。
之後要測不同指標,只要傳不同設定就好。

ts
createGidpScreenshotCompareTests({
  targetModel: 'ReutersSmartEstimate_EPS',
  stocks: ['2330', '2317', 'AAPL'],
  targetPage: '/lab/dashboard/lynch-tengrower/40338',
  modelTitleMap: {
    ReutersSmartEstimate_EPS: '【年】每股盈餘預測(LSEG/IBES)',
  },
});

這樣每個指標不用重寫一整套測試流程,只要提供:

設定用途
targetModel要測哪個指標
stocks要測哪些股票
targetPage要進哪個頁面
modelTitleMap用圖表標題找到要截圖的區塊

為什麼不截整頁?

一開始可能會想說,直接截整頁最快。

但實務上,整頁截圖很容易被干擾。

例如:

  • Header 高度不同
  • modal 跳出來
  • 某個區塊 loading 比較慢
  • 頁面 scroll 位置不一樣
  • 其他圖表還沒載入

這些都會讓 diff 變得很吵。

所以我選擇只截「目標圖表」。
這樣比對出來的差異會比較有意義。

為什麼要測多支股票?

因為圖表問題常常不會每支股票都出現。

有些股票資料很完整,看起來永遠正常。
但換成另一支股票,可能就出現:

  • 缺年份
  • 沒資料
  • 數值是 null
  • Production 有圖,Local 沒圖
  • Local 有圖,Production 沒圖

所以測試會逐支股票跑。

如果找不到圖表,也不會直接讓測試炸掉,而是記錄狀態:

狀態意思
MISSING_LOCALLocal 找不到圖表
MISSING_PRODProduction 找不到圖表
MISSING_BOTH兩邊都找不到
ERROR截圖或比對過程失敗

這樣後續看報告時,才知道問題是什麼。

pixelmatch 比對後會產生什麼?

每支股票會產出三張圖:

檔案用途
stock__local.pngLocal 新 API 的圖表
stock__prod.pngProduction 舊 API 的圖表
stock__diff.png兩張圖的差異

這比單純印 log 有用很多。

因為如果只看到:

txt
similarity: 96.3%

其實很難判斷發生什麼事。

但如果有 diff 圖,就可以比較快看出來:

  • 是數值不同
  • 是 X 軸不同
  • 是線圖位置不同
  • 是圖表沒載入
  • 還是只是很小的視覺差異

這個測試不能解決什麼?

這點我覺得很重要。

Screenshot Test 不是萬能的。

它可以幫我確認:

新舊環境畫面是否一致。

但它不能直接證明:

資料一定是正確的。

如果舊 API 本來就是錯的,新 API 跟舊 API 長得一樣,那也只是代表「畫面一致」,不代表「資料正確」。

所以我會把 Screenshot Test 定位成:

API 替換時的回歸檢查工具。

真的要確認資料正確,還是需要資料來源、後端邏輯或業務規則一起確認。

實際用下來的感想

這種測試最大的價值,不在技術有多炫。

它真正有用的地方,是把原本很模糊的討論變得比較具體。

以前可能只能說:

我覺得這張圖怪怪的。

現在可以變成:

這支股票在 Local 和 Production 的圖表差異是 12,430 pixels,diff 圖在這裡。
看起來主要差在 2025 年之後的資料。

這對溝通很有幫助。

尤其在 Legacy 專案裡,很多問題不是一句「前端修一下」就能解決。
有時候差異來自 API,有時候來自資料來源,有時候是舊邏輯本來就不清楚。

Screenshot Test 至少可以先把問題攤開來。

什麼情境適合用?

我覺得這種測試很適合用在:

情境原因
API migration可以比對改 API 前後畫面
Dashboard畫面輸出比 DOM 更重要
金融圖表數值、時間軸、單位都很敏感
Legacy refactor很難確定改完有沒有影響舊功能
新舊系統並行可以做 side-by-side comparison

如果只是一般表單、按鈕、流程,一般 E2E test 通常就夠了。

結論

這次做 Screenshot Test,讓我比較有感的是:

在複雜的 Legacy 專案裡,測試不一定一開始就要追求完美,但至少要讓風險看得見。

當資料流很亂、圖表邏輯很多、人工驗證又很容易漏掉時,截圖比對是一個蠻實用的做法。

它不能保證資料正確,也不能取代 unit test。
但在 API 替換這種高風險情境下,它可以幫助我們快速確認:

  • 哪些圖表看起來一致
  • 哪些圖表有差異
  • 哪些股票缺資料
  • 差異大概發生在哪裡
  • 問題應該往前端、後端還是資料來源追

對我來說,這不是為了把測試寫得很漂亮。
而是讓改動有證據、讓風險能被討論,也讓自己不要只靠人工慢慢點到懷疑人生。


這篇文章有幫助嗎?