對於軟體架構師,測試不僅用來找 Bug,它們也是系統的一個元件。
所有測試都具備相同架構特徵:
- 遵循依賴原則: 測試非常具體且詳細,它們總是依賴於被測試的程式碼。在架構圖中,測試位於最外層(比 UI 和框架更外層)
- 可獨立部署: 測試通常部署在測試環境中,而非生產環境
一、脆弱的測試問題 (The Fragile Tests Problem)#
如果測試沒被視為架構一部分來精心設計,就會發生「脆弱的測試問題」。
- 系統僵化: 當開發者修改一個通用元件(例如更改一個欄位名稱),導致數百個測試同時失敗
- 抗拒改變: 當「修復測試」高於「修改程式碼」的成本時,團隊會抗拒重構。系統因此腐化
原因: 測試與系統結構產生了強烈耦合(Structural Coupling)。測試知道太多系統內部的細節了。
二、解決方案:測試 API (The Testing API)#
為避免測試變脆弱,我們須將「測試結構」與「應用程式結構」解耦。 方法是建立一個特定的 Testing API。
- API 的角色: 這是一組互動者(Interactors)和介面轉接器(Interface Adapters)的超集合(Superset)
- 功能:
- 允許測試直接驗證所有業務規則
- 繞過 UI: 測試不應該透過登入畫面來驗證業務邏輯
- 繞過安全性限制: 讓測試能直接存取狀態
- 目的: 隱藏應用程式的具體結構。當應用程式重構(如類別拆分、改名)時,只要 Testing API 保持不變,測試就不會壞
三、具體與抽象的演化#
測試程式碼與產品程式碼遵循著相反的演化路徑,這也是為什麼它們需要被解耦:
- 產品程式碼: 傾向於越來越抽象(Abstract) 和 一般化(General),以重用邏輯並應對多變的需求
- 測試程式碼: 傾向於持續保持具體(Concrete) 和 特定化(Specific),因為它們必須驗證特定的情境與數據
不要讓你的測試依賴於易變細節(如 DOM 結構或特定函數名稱)。
透過設計一層專屬的 Testing API,我們可保護測試免受系統結構變更的影響,
確保測試是幫助我們重構的朋友,而不是阻礙變更的敵人。