如何為你的 code 加測試?
了解測試策略
🧩 TDD (Test-Driven Development):
一般在導入測試時,會提倡使用TDD方式來進行,也就是「先寫測試、再開發」,由測試驅動程式結構設計,可以大幅避免寫出可測試性低的程式碼,其主要的概念為 Red => Green => Refactor,在每個功能重複執行該流程,以完成開發。
- Red 階段: 先寫一個測試並執行,此時測試並不會通過,因為尚未進行任何開發(沒有程式碼可以被測試),但該階段可以讓開發者想清楚該功能的目的。
- Green 階段: 依據測試寫出至少能通過測試的程式碼,並執行測試,此時測試會通過,但程式碼尚未非常完善,僅能符合測試的條件。
- Refactor 階段: 重構上一步的程式碼,補充細節處理、提升可讀性等等,使程式碼更完整。
優點:
- 測試提供保護網,讓開發者更有信心進行重構
- 長期可降低除錯與修復成本
- 減少 over-engineering,先思考行為再寫實作 (避免寫太多暫時不需要的功能,專注於當下需求)
- 提升程式碼可測性與模組化程度,自動建立最小可測單元 (減少耦合)
挑戰:
- 初期學習曲線較高
- 不適合快速 prototyping
- 對需求模糊、變動頻繁的功能會增加反覆改動成本
適用情境:
- 需求明確、邏輯明確的功能(例如:演算法、資料驗證、表單流程)
- 關鍵業務邏輯(如:交易計算、權限驗證)
- 想設計出鬆耦合、可測性高的程式結構
- Refactor 前確保功能行為不變
🧩 TAD (Test After Development):
TAD是一種常見的測試導入方式,也就是開發完成後再加入測試,通常執行TAD會十分困難,因為程式碼可能大多是難以測試的結構,需要進行重構後才能進行撰寫測試(但仍可以先以局部重構的方式慢慢加入)。
優點:
- 開發較快速直覺,無需先設計測試架構,可以快速 prototyping
- 對於探索式開發較有彈性
- 對需求不穩定的專案較友善
挑戰:
- 測試容易被忽略或延後,累積技術債
- 程式設計容易耦合難測,需重構後才能寫測試
- 補寫的測試可能不完整,無法涵蓋所有邊界與例外情況
- 測試維護成本提高,變更 A 功能可能造成 B 測試失效
適用情境:
- 原型階段或需求還在變動
- UI 互動、視覺細節尚未定案
- 開發者想先掌握整體邏輯流程
- 維護舊有系統時(寫回歸測試)
測試策略選用建議
大致建議:
新開發→ TDD ,既有專案→ TAD
| 類型 | 優先建議策略 |
|---|---|
| 關鍵業務邏輯 | TDD |
| 表單驗證 / 演算法邏輯 | TDD |
| UI 操作 / 非穩定需求 | TAD |
| 舊專案補測試 | TAD |
| 快速開發或驗證原型 | TAD(+ 後續補 TDD) |
- 在實務中,可視情況混用 TDD 與 TAD,不必強求一致。
- 不論選用哪一種策略,測試的覆蓋度與品質才是關鍵。
測試重構原則
為了寫出容易測試的程式碼,有以下幾個原則可以依循著進行重構:
| 原則 | 解釋 | 實例 |
|---|---|---|
| 純函式優先 | 無副作用、傳入固定輸入回傳固定結果 | filterData(data, keyword) |
| 資料傳入傳出 | 不直接修改全域或組件變數 | 避免 this.xxx = ... |
| 分離 concern | UI 和邏輯分離 | onClick 只負責觸發,邏輯交給外部 utils |
| 依賴注入 | 函式接收外部依賴而不是硬編寫 | api.fetchList() 改為從參數傳入 |
| 小函式化 | 拆分邏輯,一次只做一件事 | 一個函式只處理篩選,一個只處理排序 |
若概括這些原則來說,就是讓函式不要有「副作用」,因為副作用會導致程式執行過程中,難以察覺的外部變化或影響,並且對這樣的函式寫測試時,會需要添加大量的 Mock 才能進行,測試成本變得很高,一旦修改也很容易造成測試損壞無法進行,所以函式應以"只操作傳入參數,並傳出結果"的方式撰寫 (像 utils 那樣可重用)。