2026/06/23
最近在處理一個舊專案的 API 替換。
這個專案的資料流不算乾淨。
頁面上有很多圖表、很多指標、很多股票,資料會經過一層又一層轉換,最後才變成畫面上的線圖或長條圖。
所以當後端說:
新 API 的格式會跟以前一樣。
我第一個反應其實沒有比較放心,反而是:
那我要怎麼確認真的一樣?
在 Legacy 專案裡,最可怕的通常不是整個畫面爆掉。
整個爆掉很好發現。
麻煩的是這種:
這種問題很難只靠人工慢慢點。
所以我做了一個 E2E Screenshot Test,用來比較:
| 環境 | 代表 |
|---|---|
| Local | 新 API |
| Production | 舊 API |
簡單講,就是讓 Playwright 同時打開兩邊,登入、進同一頁、切同一支股票,然後把同一張圖表截下來比對。
---
這個測試不負責證明資料一定正確。
它主要想回答一個比較務實的問題:
我把 API 換掉之後,畫面有沒有跟原本不一樣?
這對圖表類功能很重要。
一般 E2E test 可以檢查:
1await expect(page.getByText('每股盈餘預測')).toBeVisible();但這只能證明「圖表標題有出現」。
它沒辦法告訴我:
所以我選擇直接截圖,比像素差異。
---
整個測試流程其實不複雜:
1開 Local 頁面
2開 Production 頁面
3 ↓
4兩邊都登入
5 ↓
6進到同一個 Dashboard
7 ↓
8切換同一支股票
9 ↓
10等待資料載入
11 ↓
12把頁面往下捲,讓圖表都載入
13 ↓
14找到指定圖表
15 ↓
16兩邊各截一張圖
17 ↓
18用 pixelmatch 比對
19 ↓
20輸出 local / prod / diff 圖片和報告我把它包成一個 factory function。
之後要測不同指標,只要傳不同設定就好。
1createGidpScreenshotCompareTests({
2 targetModel: 'ReutersSmartEstimate_EPS',
3 stocks: ['2330', '2317', 'AAPL'],
4 targetPage: '/lab/dashboard/lynch-tengrower/40338',
5 modelTitleMap: {
6 ReutersSmartEstimate_EPS: '【年】每股盈餘預測(LSEG/IBES)',
7 },
8});這樣每個指標不用重寫一整套測試流程,只要提供:
| 設定 | 用途 |
|---|---|
| `targetModel` | 要測哪個指標 |
| `stocks` | 要測哪些股票 |
| `targetPage` | 要進哪個頁面 |
| `modelTitleMap` | 用圖表標題找到要截圖的區塊 |
---
一開始可能會想說,直接截整頁最快。
但實務上,整頁截圖很容易被干擾。
例如:
這些都會讓 diff 變得很吵。
所以我選擇只截「目標圖表」。
這樣比對出來的差異會比較有意義。
---
因為圖表問題常常不會每支股票都出現。
有些股票資料很完整,看起來永遠正常。
但換成另一支股票,可能就出現:
所以測試會逐支股票跑。
如果找不到圖表,也不會直接讓測試炸掉,而是記錄狀態:
| 狀態 | 意思 |
|---|---|
| `MISSING_LOCAL` | Local 找不到圖表 |
| `MISSING_PROD` | Production 找不到圖表 |
| `MISSING_BOTH` | 兩邊都找不到 |
| `ERROR` | 截圖或比對過程失敗 |
這樣後續看報告時,才知道問題是什麼。
---
每支股票會產出三張圖:
| 檔案 | 用途 |
|---|---|
| `stock__local.png` | Local 新 API 的圖表 |
| `stock__prod.png` | Production 舊 API 的圖表 |
| `stock__diff.png` | 兩張圖的差異 |
這比單純印 log 有用很多。
因為如果只看到:
1similarity: 96.3%其實很難判斷發生什麼事。
但如果有 diff 圖,就可以比較快看出來:
---
這點我覺得很重要。
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 替換這種高風險情境下,它可以幫助我們快速確認:
對我來說,這不是為了把測試寫得很漂亮。
而是讓改動有證據、讓風險能被討論,也讓自己不要只靠人工慢慢點到懷疑人生。