iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Software Development

AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質系列 第 2

Day 02:TDD 的本質沒變——Red-Green-Refactor 三步驟快速複習

  • 分享至 

  • xImage
  •  

前言:三個步驟大家都會背,但真的知道每一步在防什麼嗎?

「Red-Green-Refactor 誰不知道?寫一個失敗的測試、讓它通過、再重構,這種基本功還需要花一篇文章講?」

如果你已經寫過幾年測試,這句話大概是你看到這篇標題時的第一反應。但我想先問一個問題:**Red 階段真正在確認的是什麼?**如果你的答案是「確認測試會失敗」,那只答對了一半——Red 階段確認的不是「測試失敗了」這個結果,而是「這個測試有能力偵測到錯誤」這件事本身。這兩者聽起來很像,實際上是完全不同的檢查。

昨天談到一個案例:一份測試的預期值是從待測程式碼自己算出來的,不管實作寫對還是寫錯,它永遠是綠燈。那份測試從來沒有真正紅過——如果作者曾經認真看過 Red 階段那顆紅燈長什麼樣子,這個問題在寫下第一行程式碼之前就會被抓到。**三個步驟的名字大家都背得出來,但每一步真正要確認的事,恰好是 AI 最容易悄悄跳過的地方。**這篇要做的事很單純:退回原點,把 Red、Green、Refactor 各自的目的講清楚,作為接下來 29 天檢查「AI 有沒有偷跳步」的判斷基準。

今日目標

  • 重新理解 Red 階段的目的:不只是「測試會失敗」,而是「確認測試本身有效」
  • 重新理解 Green 階段的目的:用最小手段讓測試通過,而不是提前寫出「完整」的實作
  • 重新理解 Refactor 階段的目的:在測試提供的安全網之下,才能放心改善程式碼結構
  • 看一組對照範例,具體感受「跳過 Red 直接寫 Green」會漏掉什麼
  • 對 AI 時代這三步驟各自會遇到的挑戰先有個底,作為後面篇章的預告

Red:先確認測試會失敗,而且是「因為對的理由」失敗

Kent Beck 在《Test-Driven Development: By Example》裡把 Red 階段描述為「寫一個小小的、跑不過的測試,甚至一開始可能連編譯都過不了」。這句話容易被簡化成「先寫測試」,但真正的重點在「跑不過」——而且不是隨便一種跑不過都算數。

Red 階段要確認兩件事:

  1. 測試真的會失敗,而不是因為測試本身有 bug(例如斷言邏輯寫反、根本沒有執行到待測程式碼)而意外通過。
  2. 測試失敗的原因,是你預期的那個原因——也就是「這個功能還沒實作」,而不是環境設定錯誤、測試資料寫錯、或是引用了不存在的類別導致的例外。

換句話說,Red 階段是在幫測試本身做一次「測試的測試」。一份從來沒有紅過的測試,你永遠不知道它有沒有能力在實作寫錯的時候變紅——它可能寫了斷言、跑起來也是綠燈,但那顆綠燈可能只是因為斷言恆真、或是根本沒有真正呼叫到待測邏輯。看到紅燈,是你唯一能確認「這份測試有辨別力」的時刻,過了這個時間點再想確認就難了,因為之後每次跑測試看到的都是綠燈,你沒有機會反過來驗證它會不會變紅。

Green:用最小手段讓測試通過,不是提前寫出「完整」的實作

Beck 對 Green 階段的原話更直白:「盡快讓測試通過,過程中犯的任何罪都可以先不管」。這句話常被誤解成「隨便寫都行」,但真正的意圖是刻意壓抑「順便把其他情境也一起處理好」的衝動

為什麼要這樣做?因為 Green 階段的唯一目標,是驗證「這個測試加上這段最小實作,兩者搭配起來是對的」。如果你在寫 Green 的當下就把三種邊界情境、兩種例外處理都一起寫進去,你就沒辦法確定測試綠燈是因為你剛剛寫的那個測試通過了,還是因為你多寫的那些程式碼裡剛好也包含了讓它通過的邏輯。最小手段讓測試通過,換來的是「這次的紅燈變綠燈,跟我剛剛寫的這段程式碼有直接對應關係」這件事的確定性。

至於「犯的罪」——寫死回傳值、複製貼上邏輯、暫時忽略某個分支——這些不是被鼓勵的壞習慣,而是被刻意允許的暫時債務,債務會在下一步被處理。

Refactor:有安全網才能真正改善結構

Refactor 階段是三步驟裡最常被跳過的一步,原因很現實:前兩步都有測試盯著(Red 要看到失敗、Green 要看到通過),但 Refactor 階段看起來「已經是綠燈了,好像做完了」。

但 Refactor 真正的定義是:在不改變外部行為的前提下,改善程式碼的內部結構。這句話裡「不改變外部行為」這個前提,靠的正是前面 Red 階段確認過的那份有辨別力的測試——因為你已經知道這份測試會在行為跑掉的時候變紅,你才能放心去動程式碼結構,而不用擔心自己在重構的過程中不小心改壞了行為。

這也是為什麼 Refactor 階段的存在,反過來證明了 Red 階段不能被跳過:如果測試本身沒有辨別力,Refactor 階段就沒有安全網可言,你做的每一次「重構」其實都是在裸奔改程式碼,只是你自己不知道。

一組對照:跳過 Red 直接寫 Green,漏掉了什麼

下面用一個簡單案例,比較「照順序走過 Red」跟「直接寫出通過的程式碼跟測試」兩種做法的差別。情境是實作一個「檢查會員是否符合升級門檻」的函式,門檻是累積金額達到 1000 才算符合。

❌ 跳過 Red,測試和實作同時生成,從沒看過紅燈:
function checkUpgradeEligible(amount) {
  return amount >= 1000;
}

test("checkUpgradeEligible", () => {
  expect(checkUpgradeEligible(1000)).toBe(true);
});
→ 這份測試從第一次執行就是綠燈。
  如果把 >= 誤寫成 >,這個測試依然是綠燈,
  因為它只測了剛好等於門檻的那個值,
  沒有人在寫測試的當下確認過「如果實作寫錯,這裡真的會變紅」。

✅ 先寫測試、看紅燈、再補最小實作:
test("累積金額等於門檻時應符合升級資格", () => {
  expect(checkUpgradeEligible(1000)).toBe(true);
});
// 執行:ReferenceError,checkUpgradeEligible 尚未定義 → 紅燈,符合預期
// 補最小實作:
function checkUpgradeEligible(amount) {
  return amount >= 1000;
}
// 執行:綠燈
// 再追加一個邊界測試,確認測試真的有辨別力:
test("累積金額差一元未達門檻時不應符合升級資格", () => {
  expect(checkUpgradeEligible(999)).toBe(false);
});
→ 這一步才是關鍵:如果把 >= 誤寫成 >,
  第一個測試依然綠燈,但第二個測試會告訴你「等於門檻」那個案例
  其實對實作的正確性沒有鑑別力,必須靠邊界情境的測試補上。

這組對照要說的不是「要多寫一個測試案例」這麼表面的事,而是:只有在你認真經歷過「這個測試會不會紅」的檢查之後,才會發現原本那份測試根本測不出等號寫錯這種問題。跳過 Red 直接生出一組「看起來對」的測試加實作,恰好把這個檢查的機會整個跳過了。

Red 階段確認的不是測試失敗,而是測試有沒有能力偵測錯誤;少了這一步,Green 階段的通過跟 Refactor 階段的安全感,都建立在一個你從未驗證過的假設上。

AI 時代的初步觀察:三步驟各自會遇到什麼挑戰

這三個步驟的定義沒有變過,但 AI coding agent 介入之後,每一步都多了一種新的失守方式,這是接下來幾天要分別展開的內容,這裡先各留一句話當引子:

  • Red 階段:AI 傾向把測試跟實作一起生成、一起丟給你看綠燈結果,你很容易根本沒看過那顆測試曾經是紅的(Day 06、Day 07 會展開)。
  • Green 階段:AI 為了讓測試通過,有時候走的「最小手段」不是簡化邏輯,而是直接把測試預期的答案寫死進實作裡(Day 08、Day 09 會展開)。
  • Refactor 階段:AI 常常在沒有明確要求、也沒有確認過安全網夠不夠的情況下,「順手」重構了一大段程式碼(Day 10 會展開)。

這三個挑戰的共同點,留到明天先講第一個切入點:AI 能不能加速「寫測試」這個動作,跟這份測試值不值得信任,是兩件事

今日思考題

回想你最近一次寫測試的經驗:你有沒有真的停下來看過那顆測試變紅的那一刻?還是測試寫完、實作也順手寫完,第一次執行就已經是綠燈了?如果是後者,你怎麼確定這份測試在實作寫錯的時候真的會告訴你?

今日重點回顧

  • Red 階段的目的不是「看到測試失敗」,而是確認這份測試有能力在實作出錯時偵測到錯誤——這件事只有在紅燈那一刻能被驗證
  • Green 階段用最小手段讓測試通過,目的是確保「這次的紅燈變綠燈」跟你剛寫的程式碼有直接對應關係,而不是提前寫出完整實作
  • Refactor 階段的安全感建立在 Red 階段確認過的有效測試上——沒有辨別力的測試,重構就是裸奔
  • 一組對照範例顯示:跳過 Red 直接產出「看起來對」的測試加實作,會漏掉「這份測試到底有沒有辨別力」的檢查機會
  • AI 時代三步驟各自會遇到不同的失守方式,接下來的篇章會逐一展開

明日預告

明天要談 AI 讓「寫測試」這個動作本身變快帶來的影響:AI 讓「寫測試」變快,但「寫對測試」的門檻沒有變低——動作變快跟品質變好,從來不是同一件事。


延伸閱讀:本系列另外三個並行系列——《用 AI Agent 重構一套無框架的 legacy PHP 系統》《讓 AI Agent 維護一個 Open Source Project》《AI 寫 Code 之後,我們還需要 Software Architecture 嗎?》——會從不同角度處理相關的經驗,有興趣可以一起追。


上一篇
Day 01:系列介紹——AI 時代為什麼更需要 TDD
系列文
AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質2
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言