「請幫我實作這個功能,記得寫測試。」這句話丟給 AI,十之八九會得到一份「實作 + 測試」同時完工的回應——兩個檔案一起生成,測試全綠,看起來效率很高。
但如果你把這 19 天讀到現在,應該已經開始對這個畫面感到不安:這代表 Red 階段從沒發生過,Green 階段也不是「先讓一個空的測試通過」再逐步補上邏輯,而是實作跟測試同時從無到有一次寫完。 循環沒有被走過,只是被交出了一個看起來符合循環結果的產物。今天要把這個現象拆開來看:為什麼 AI 會這樣做、這對測試品質造成什麼具體傷害。
這不是 AI 偷懶,而是它的預設運作模式本來就是這樣:接到一個完整的需求描述,產生一個完整的回應,是最符合「把任務做完」這個目標的路徑。TDD 要求的「先寫一個會失敗的測試、看著它失敗、再寫最小的實作讓它通過」,這中間每一步都要求輸出一個「還不完整」的中間狀態,並且要求使用者在中間狀態介入確認——這跟「一次把事情做完」的預設傾向是兩種完全不同的工作模式。
沒有人明確要求分階段,AI 就會預設選擇最短路徑:一次生成看起來完整、能動、有測試的成品。 這在多數任務裡是合理的效率選擇,但 TDD 的價值恰恰建立在「刻意放慢、分階段驗證」這件事本身,兩者的目標函式從根本上不一致。
這裡有個容易混淆的地方要先講清楚。「一次到位」產出的測試,語法上完全可能長得跟真正走過 TDD 循環的測試一模一樣——一樣有 Arrange/Act/Assert,一樣斷言了看起來合理的值。差別不在測試程式碼的外觀,而在於這個測試有沒有真的被驗證過「它有能力抓到錯誤」(Day 06、Day 07 講過的 Red 階段的價值)。
一份走過循環的測試,紅過、綠過、被重構過,每一步都留下一個「確認過」的痕跡。一份一次到位的測試,從第一次執行就是綠燈,沒有人知道如果實作寫錯了,這個測試會不會抓到——因為它從來沒有機會證明這件事。
用一組對照來看這個差異:
❌ 一次到位:
AI:「已完成折扣計算功能,附上實作與測試,測試全部通過。」
→ 實作跟測試同時誕生,測試從未在「實作不存在或寫錯」的情況下
被執行過,沒有人知道它有沒有偵測錯誤的能力
✅ 走過循環:
AI:「第一步:寫了一個測試,目前實作還沒寫,執行結果如下——
測試失敗,錯誤訊息是『找不到 calculateDiscount 方法』。
第二步:補上最小實作,測試通過,執行結果如下。
第三步:確認測試涵蓋了主要情境後,進行以下重構……」
→ 每一步都有可以被檢查的中間產物,
測試的有效性在 Red 階段就已經被證明過
一次到位省下的時間,其實是把「確認測試有沒有效」這個步驟的成本,轉嫁給了事後的人工複查——而複查一份已經全綠的測試,比看著它從紅變綠困難得多,因為你已經失去了「刻意讓它失敗」這個最直接的驗證機會。
第一種傷害最直接:假陽性測試更容易混進來而不被發現(Day 04、Day 05 講過的樣貌)。既然沒有人看過這個測試「紅」的樣子,空斷言、恆真斷言這類問題就少了一道天然的過濾機制。
第二種傷害比較隱蔽:測試跟實作之間的耦合會變得更緊。當實作跟測試在同一個生成過程裡誕生,AI 很容易讓測試直接反映實作的內部結構(例如測試斷言了某個中間變數該有的值),而不是反映外部該有的行為——因為兩者是同一次思考的產物,沒有經過「先只想清楚行為,再回頭想實作」這個分離過程。
第三種傷害最容易被忽略:Refactor 階段名存實亡。Day 10 講過 AI 容易在 Refactor 階段順手做沒被要求的調整,但更根本的問題是——如果一開始就沒有分階段,「重構」這個動作根本沒有一個明確的起點可以對照,變成實作寫完就直接結束,沒有人回頭檢視「這段程式碼現在的樣子,是不是已經是它該有的樣子」。
回想你上一次請 AI 實作一個功能並附上測試:你拿到的是一次到位的完整產物,還是走過分階段循環的過程?如果是前者,你有花額外的時間去驗證那些測試「有沒有偵測錯誤的能力」,還是看到全綠就直接接受了?
明天要給出具體解法:怎麼設計 prompt 跟流程,強迫 AI 把一個任務拆解成小步驟,一步一步走完紅綠重構循環,而不是一次到位。