認識 Test-Driven Development (TDD)
自從踏入軟體開發職場後,我最早認識到的開發流程之一就是 TDD,我也認為他應該是每位軟體開發者都應該知道的開發流程,即使在實務上並非所有專案或情境都適合採用,但我覺得能有 TDD 的概念也很棒。
TDD 的核心價值並不只是確保功能能正常運作,而是透過「先寫測試」的方式,引導開發者思考程式介面的使用方式,進而提升程式碼的可測試性與可維護性。
不過,TDD 並不是一套完整的軟體設計方法,它無法直接保證系統架構的最佳化,但我認為能具備 TDD 的思維,本身就是建立良好工程習慣的重要一步。
Test-Driven Development (TDD)
TDD 全名是 Test-Driven Development,也就是「測試驅動開發」,是一種以測試驅動開發的開發流程。
在傳統開發中,我們通常會先把功能寫出來,最後再補上測試,或是靠手動操作確認功能是否正常。然而在實際專案裡,這種方式常常會遇到一些狀況:明明 A 功能沒動,修改 B 之後 A 就突然壞掉,或功能在自己環境看起來都正常,一到測試階段 QA 那邊就冒出一些莫名其妙的 bug。
TDD 的觀念剛好反過來。它強調 「先寫測試,再進入開發」。第一次聽到時,很多人都會覺得不可行:「功能都忙不完了,哪有空先寫測試?」但如果換個角度思考,其實 TDD 的流程跟 QA 在專案初期會先撰寫 Test Case 的概念非常接近:
- 先明確定義功能應該具備的行為與邊界
- 再依照這些行為規格實作程式碼
- 讓每一行新程式碼都有對應的驗證依據
- 在修改或擴充功能時,能即時發現是否破壞既有行為
TDD 做的事情,就是把這套流程提前到工程師手上,讓開發在寫第一行程式碼之前,就先釐清:「這個功能到底要做什麼?」、「哪些情境應該通過?」、「哪些行為應該失敗?」。
因此,先寫測試並不是單純在完成測試本身,而是在迫使開發者先釐清介面與行為的預期,再著手撰寫程式碼。這種方式自然也讓程式碼設計更清晰,也避免過早陷入實作細節。
TDD 精神
不過 TDD 的核心精神並不是寫測試本身,而是透過測試來驅動設計、驗證行為、降低開發風險。
其中包含三個重要精神:
-
以「行為」為中心的開發思維(Behavior-Oriented Thinking)
在 TDD 中,測試案例不是用來檢查程式,而是用來描述功能應該如何運作(specification)。
- 測試等於需求的具體描述
- 測試寫的不是「怎麼做」,而是「應該發生什麼事」
- 讓開發者先思考 使用者角度 的行為,而不是陷入實作細節
-
反覆的小步前進(Small Steps, Fast Feedback) TDD 最核心的原則之一就是「Small Steps」
- 每一次只加入一點點新行為
- 每一次只寫一個失敗測試
- 每一次只寫最小的程式碼讓它通過
這麼做是為了讓開發過程保持可控、可預測、可追蹤,以及回饋,讓你可以快速發現問題,而不是等到最後才發現整包炸掉。
-
透過測試確保設計的可維護性(Design for Testability) TDD 的過程會透過測試讓程式碼呈現良好的設計特色,像是:
- 模組化
- 小型、可重複使用的函式
- 低耦合、高內聚 而這些都不是你刻意做的,是因為『你沒辦法寫出難測試的程式碼,而能被測試的程式碼自然就設計得比較好。』
開發流程
接下來進入主軸,TDD 最廣為人知的流程就是 Red → Green → Refactor,也有人稱它為「紅綠重構循環」。
這並不是一個一次跑完的流程,而是一個會在開發過程中不斷重複的小循環。
這個循環的重點不在於「寫很多測試」,而在於用測試來限制與引導程式碼的成長方向。
Red:先讓測試失敗
第一步永遠是:先寫一個會失敗的測試。
這個測試通常描述的是「一個尚未存在的行為」:
例如在開發一個下單流程時,Red 階段通常會明確寫出像這樣的測試情境:
- 購物車為空時,不該前往結帳,應該拋出錯誤
- 商品庫存不足時,不應該產生訂單
- 成功下單時,應該回傳訂單編號
在這個階段,你不需要關心實作方式,只需要專注在應該表現出什麼行為
Green:用最小成本讓測試通過
接下來這個目標很單純,就是讓剛剛那個失敗的測試變成通過(Green)。
這裡有一個 TDD 初期最容易有誤解的地方:
Green 階段 不追求漂亮的設計,也不追求完整性
因此在這個階段:
- hardcode 是合理的
- 邏輯重複是可接受的
- 結構不好看是預期中的狀態
在實務專案裡,這樣做可以避免一個常見問題:
為了「可能會用到」,提前設計過度彈性的結構。
Green 階段實際帶來的好處是:
- 功能只成長到「目前需要的程度」
- 每次 commit 的變動範圍很小
- 問題一出現,幾乎可以立刻定位
如果測試是綠的,就代表在目前定義的規格下,這個行為是成立的。
Refactor:當行為能夠被測試,才開始談設計
當測試全綠之後,才進入 Refactor 階段。
因為到了這個時間點,功能的行為基本已經被測試固定住,所以你可以安心重構,而不用擔心改壞功能。
在這個階段通常會做:
- 抽出重複邏輯
- 拆分過於肥大的 function / class,保持 SRP
- 重新命名,讓意圖更清楚
- 調整模組邊界,降低耦合
如果你發現「不敢重構、改不動」,通常代表測試寫得不夠貼近行為,或測試層級不對。
重複循環
在真實專案中,需求幾乎不可能一次定義完成。
完成一次 Red → Green → Refactor 後,你不會停下來。
TDD 的循環設計,讓你可以用非常低的成本去回答:
- 新需求加進來,會不會影響舊行為?
- 目前的設計能夠支援嗎?
- 是時候重構,還是先忍一下?
每一次 Red → Green → Refactor,都只處理一個小問題,讓系統以「可回頭」的方式前進。
久了之後你就會發現:
- 程式碼不是一次設計完成,而是隨著需求在多次迭代中逐步被推導出來
- 重構變成一種日常行為,而不是等到累積成大型風險才一次處理
- 測試成為系統最可信、可隨迭代持續更新的行為文件
Licensed under CC BY-SA 4.0
Unless otherwise noted, content on this site is licensed under CC BY-SA 4.0. You are free to share and adapt with attribution.
