一個回歸測試套件可以在每一步都事出有因的情況下,逐漸變得難以信任。在期限壓力下,這個模式再熟悉不過:重現一個缺陷、加一個測試、確認修復,然後繼續趕工。![]()
多年各自合理的局部決策,會留下一個從來沒有被當成整體設計過的套件——好幾個測試守護著同一種行為、老測試守護著早已不存在的需求,還有脆弱的測試因為實作改動而失敗,而不是因為產品真的壞了。
直覺可能是不斷增加覆蓋率,因為測試越多似乎保護越強。但測試數量與信心並不是同一回事。當失敗變得嘈雜、執行時間過長、沒有人說得出哪些測試代表重要風險時,套件就開始掩蓋資訊,而不是提供資訊。有時候,刪掉一些測試反而會讓信心上升,因為留下來的套件更容易理解、更容易維護、也更容易被相信。
重複是指好幾個測試實質上證明同一種行為,而且因為同樣的原因失敗。這些是合併的候選對象,而不是盲目保留每一種變體。脆弱則不同:測試仍然守護著一個正當的需求,但它與實作細節綁得太緊——內部函式名稱、確切的生成措辭、DOM 結構,或其他不影響使用者可見行為卻可能改變的細節。正確的處置通常是圍繞契約或可觀察的行為重寫斷言,而不是把保護整個刪掉。
過時是指需求本身已經前進,但它的測試沒有跟上。針對已停用的工作流程、已移除的政策或舊版回應的測試,可能會主動造成誤導,因為它的失敗已不再代表產品缺陷。因此這三類需要不同的行動:重複的可以合併、脆弱的可以重構、過時的可以在確認其確實無關後刪除。關鍵的區分在於:我們移除的是多餘的測試,而不是必要的證據。
刪除任何測試之前,先問:**它是不是某個有意義風險的唯一防線?**一個寫得彆扭或很慢的測試,看似可以捨棄,直到我們發現它是唯一在檢查多國語言編碼、罕見的授權邊界、逾時情境,或其他低頻但高衝擊場景的地方。執行頻率不等於重要性。如果沒有其他測試守護那個風險,更好的決定可能是保留它、改善它,或降低執行頻率,而不是移除它。
第二個問題是:**這個測試所守護的歷史缺陷,是否已被更高層接手?**最初為某個特定缺陷補上的回歸測試,一旦同樣的行為已由更強的契約、整合或端對端測試保護,就可能變得多餘。刪除之前,我想先找出當初的失敗、定位它現在的保護所在,並在可行的情況下刻意破壞或模擬該行為,確認留下來的測試真的攔得住它。唯有如此,移除較舊的測試才能降低維護成本,而不會悄悄重新打開一條舊的逃逸通道。
套件重構不只是讓 CI 跑得更快的練習。套件的結構應該讓它的優先順序一目了然:快速且確定的檢查可以持續執行,整合與契約測試可以守護重要邊界,而昂貴或特殊的場景則以合適的節奏執行。移除重複的 setup、合併等價的案例、替換綁定實作的斷言,以及把昂貴的測試搬到正確的層級,都能在不減少有意義覆蓋率的前提下縮短執行時間。
可讀性也基於同樣的理由。另一位 QA 工程師應該能夠檢視整個套件,理解它保護了哪些風險,而不是去重建多年來一個個缺陷修復的歷史。目標是一個形狀跟著風險走的套件,而不是跟著每個人在撰寫當天心情走的套件。因此刪減測試反而可能提高信心:當每一個留下來的測試都有明確的目的、有依據的層級,以及一個會告訴團隊值得調查線索的失敗時,更少的測試可以提供更清楚的證據。
舉例來說,假設套件裡有12 個針對同一個登入驗證的獨立測試,每個都重複著 setup 並檢查略有不同的無效輸入。重構可以把它們合併成一個參數化測試,包含重要的輸入案例,在移除重複程式碼並縮短執行時間的同時,保留相同的風險覆蓋。結果也更好讀:當測試失敗時,QA 可以立刻看出是哪一條驗證規則壞了,而不必在好幾個幾乎一模一樣的測試中翻找。
| 測試 | 處置 | 理由 | 影響 |
|---|---|---|---|
| 產品問答欄位(4 個重複) | 合併為 1 個契約測試 | 同一條規則,同一種失敗模式 | −3 個測試,覆蓋率不變 |
| 內部快取函式名稱斷言 | 刪除 | 綁定實作,脆弱 | 應改為斷言外部行為 |
| 已停用的舊版退貨文案 | 刪除 | 規則已移除,無人維護 | 無 |
| 多國語言編碼(唯一防線) | 保留,降低頻率 | 沒有其他測試守護此風險 | 證據保留,改為每晚執行 |
| 逾時後的非 JSON 回應 | 併入契約清單 | 與契約測試重疊 | 移除重複的整合測試 |
套件瘦身紀錄讓刪除成為一個可審計的 QA 決策,而不是例行的清理。每一筆紀錄都記下測試、採取的處置、原因,以及覆蓋率之後的走向:四個重複的產品問答檢查合併為一個契約測試,因為它們守護同一條規則;內部快取斷言被移除,因為它脆弱,應改為驗證外部行為;一個已停用的退貨測試則消失,因為底層規則已不復存在。重要的是,瘦身並不等於刪除——多國語言編碼測試被保留下來,因為它是該風險唯一的防線,只是降低執行頻率;而逾時後的非 JSON 案例則併入契約套件,因為那一層已經守護著同一個邊界。因此,這份紀錄回答了每次移除之後最重要的問題:現在由誰來守護這個風險?