「什麼都測」與「只測部分」各自有明確的代價。追求覆蓋率會產出大量測試 getter 與欄位映射的測試,維護成本高而保護力低;不看覆蓋率則沒有依據說明業務邏輯已經被驗證過。

兩者的共同問題是把測試策略當成單一決定。策略跟著架構層走之後,同一個問題會分解成五個各有答案的小問題。

問題的根源

全面單元測試的問題是,重構 ViewModel 內部實作時,大量測試跟著壞掉,但行為根本沒變。全面 BDD 的問題是,Domain 層邊界條件很難透過業務語言的場景完整覆蓋。

不同層級的程式碼,用不同的測試方法。

五層架構的測試分工

Layer 1(UI/Presentation) 只針對關鍵互動流程撰寫整合測試。判斷標準:流程失敗是否影響核心業務、是否需要多步驟操作、是否涉及金流或敏感資料。靜態展示頁面、簡單列表交給人工測試。

Layer 2(Application/Behavior) 只針對複雜轉換邏輯撰寫單元測試。判斷標準:轉換是否包含條件判斷、計算邏輯、多個來源資料,或邏輯超過十行。簡單的 DTO 欄位直接映射,不需要獨立測試,由 UseCase 層間接覆蓋。

Layer 3(UseCase) 所有業務場景都必須撰寫 BDD 測試,沒有例外。每個 UseCase 至少涵蓋一個正常流程、兩個異常流程、三個邊界條件。格式使用 Given-When-Then,只 Mock 外層依賴,使用真實的 Domain Entity。

Layer 4(Interface) 不測試。介面只定義合約,沒有可測試的行為。

Layer 5(Domain Implementation) 複雜業務規則必須撰寫單元測試。判斷標準:是否包含業務規則驗證、計算邏輯、狀態轉換、不變量檢查。Email 格式驗證、金額範圍、訂單狀態轉換都需要完整的單元測試。純資料容器的 Entity 不需要獨立測試。

做決策的流程

確定程式碼屬於哪一層(目錄結構直接反映架構層級),然後問一個問題:

  • UI 層:這是關鍵互動流程嗎?
  • Behavior 層:這裡有複雜轉換邏輯嗎?
  • UseCase 層:直接寫 BDD 測試。
  • Interface 層:不測試。
  • Domain 層:這裡有複雜業務規則嗎?

答案是就寫,不是就跳過讓上層覆蓋。

技術性測試項目

不分層級都要納入:

  • Null 值和空集合
  • 邊界值(零、負數、最大值)
  • 異常處理(網路錯誤、儲存失敗)
  • 資料驗證(格式、範圍、必填欄位)

這些容易被忽略,但往往是上線後出問題的地方。

覆蓋率的意義

這套策略讓覆蓋率指標更有意義。UseCase 層要求行為場景覆蓋率 100%,是所有業務場景都有測試,不是追求程式碼行數百分比。Domain 層複雜邏輯要求分支覆蓋率 100%,每個分支都代表一個業務決策。整體新增程式碼維持 80% 以上。

這樣定義之後,覆蓋率的數字對應的是業務場景與業務決策的涵蓋程度,而不是被執行過的程式碼行數。換分母解決的是分母的問題——每個場景的測試有沒有偵測能力是另一個問題,要另外量

測試的穩定性

UseCase 層的 BDD 測試關注行為,重構內部邏輯只要業務行為沒變,測試就不需要動。Domain 層只有規則本身改變才需要更新。Behavior 層只測複雜的轉換邏輯,重構簡單映射不影響測試。

測試因為業務改變而失敗是它在做該做的事;因為重構而大量失敗,說明它耦合的是實作結構。

Given-When-Then 的寫法與 Mock 邊界,走 BDD 測試;兩派 TDD 對「單元」的定義,走 行為優先的 TDD