iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
Software Development

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

Day 06:Red 階段——AI 容易跳過「先看到測試失敗」這一步

  • 分享至 

  • xImage
  •  

前言:測試從沒紅過,你怎麼知道它會抓到問題?

「反正最後測試都是綠的,中間有沒有真的紅過一次,有差嗎?」

有差,而且差別很大。昨天講完假陽性測試的三種樣貌——空斷言、恆真斷言、Mock 掉待測物件本身——今天要指出一個更根本的問題:這三種假陽性,幾乎都能用同一個動作提早抓到:讓測試先真的紅一次。 如果一個測試從被寫出來的那一刻起就是綠的,代表沒有人(不管是 AI 還是人)真正驗證過「當這段邏輯是錯的、或還不存在時,這個測試會不會抓到」。

Red-Green-Refactor 的第一步之所以叫 Red,不是因為「測試失敗」這件事本身有價值,而是因為它是唯一能證明「這個測試有能力偵測到問題」的時刻。跳過這一步,後面不管寫得再仔細,都只是在賭這個測試真的有效。

今日目標

  • 理解 Red 階段真正在驗證的是什麼,不是「有沒有紅過」這麼表面
  • 看清楚 AI 一次生成「實作 + 測試」時,這個驗證動作是怎麼被跳過的
  • 認識「先寫測試」跟「先讓測試紅過」是兩件不同的事,前者不保證後者
  • 建立一個具體的習慣:怎麼在 AI 協作流程裡,把 Red 階段找回來

AI 為什麼容易跳過 Red 階段

嚴格的 TDD 循環是:先寫一個會失敗的測試(因為實作還不存在或還沒做對)→ 看著它真的失敗 → 寫最少量的程式碼讓它通過 → 重構。這個循環裡,「看著測試失敗」是一個獨立的、需要主動去做的動作。

但 AI coding agent 最常見的工作模式,是接到一個需求後,一次性生成完整的實作程式碼跟對應的測試程式碼——兩者幾乎同時出現,測試從被建立的那一刻,面對的就已經是「正確」的實作。這個測試從頭到尾沒有經歷過「失敗」這個狀態,因為失敗的前提(不完整或錯誤的實作)根本沒有存在過。

這不是 AI 偷懶,而是「一次到位」正是 AI 產出程式碼時最自然的模式——它不會主動分成「先寫一個註定失敗的骨架」跟「再補上讓它通過的邏輯」兩個步驟,除非流程明確要求它這樣做。

一個具體的落差:寫了測試,不等於驗證過測試

這裡要區分兩件容易被混為一談的事:

  • 「有寫測試」:測試檔案存在,語法正確,執行會得到一個結果(通過或失敗)。
  • 「測試被驗證過」:曾經確認過這個測試在「待測邏輯是錯的」情況下,真的會回報失敗。

「有寫測試」不代表「測試被驗證過」。一份從沒紅過的測試,通過的原因可能是邏輯真的對,也可能是斷言太弱、恆真、或核心邏輯被 mock 掉——這正好對應昨天講的三種假陽性樣貌。Red 階段做的事,就是把「這個測試看起來會抓到問題」跟「這個測試真的會抓到問題」這兩件事之間的落差找出來。

用一組對照來看這件事:

❌ 一次到位,測試從沒紅過:
AI 在同一輪回應裡,同時產出:
  function calculateDiscount(order) { /* 完整實作 */ }
  test("計算折扣", () => {
    assert calculateDiscount(order) == 150
  })
→ 測試建立的當下,實作已經是「正確」的版本,
  這個測試從沒有機會證明自己有偵測失敗的能力

✅ 先讓測試紅過,再補實作:
第一步:只寫測試,此時 calculateDiscount 還不存在(或回傳寫死的錯誤值)
        執行測試 → 失敗,且失敗訊息明確指出「calculateDiscount 未定義」
        或「預期 150,實際 0」
第二步:補上正確實作
        執行測試 → 通過
→ 這個測試曾經失敗過一次,且失敗的原因跟預期一致,
  代表它真的有能力偵測到「邏輯不對」這件事

為什麼「失敗的原因」跟「失敗」一樣重要

光是看到測試變紅還不夠,紅燈的原因要跟你的預期一致,才算真正驗證過這個測試。如果你預期測試會因為「回傳值不對」而失敗,結果它卻是因為語法錯誤、或呼叫了一個根本不存在的函式而失敗,這代表測試失敗的原因跟你以為的完全不同——你以為自己驗證了斷言的有效性,實際上只是驗證了程式碼能不能跑起來。

這也是為什麼經驗豐富的工程師在做 TDD 時,看到紅燈的第一反應不是立刻動手修,而是先讀一遍失敗訊息,確認「這是我預期的那種失敗」。AI 協作時這一步特別容易被跳過,因為 AI 產出測試跟實作的速度太快,人來不及在中間插入「先看一次失敗長什麼樣」這個動作。

怎麼把 Red 階段找回來

具體做法不複雜,但需要主動要求,不能指望 AI 自己這樣做:

  1. 要求分兩步驟產出:先只要測試,明確告訴 AI「先不要寫實作,或先給一個一定會失敗的骨架實作」,執行一次測試,確認失敗訊息符合預期。
  2. 對既有的、AI 一次到位生成的測試,做事後驗證:如果測試已經跟實作一起產生了,回頭把待測邏輯暫時改成明顯錯誤的版本(例如回傳固定的錯誤值、拿掉一段判斷),重跑測試,確認它真的會變紅。這跟 Day 05 講的「故意把待測邏輯改壞」是同一個動作,只是在這裡的定位更明確——它就是在補做被跳過的 Red 階段。
  3. 把這個驗證動作寫進流程規則:不要每次都靠人記得去做,可以在 prompt 或專案規範裡明講「新增測試後,先確認過失敗案例再回報完成」。

Red 階段本身不產出任何最終會留在程式碼庫裡的東西——它產出的是「這個測試值得信任」這件事的證據。 少了這個步驟,接下來的 Green 跟 Refactor,都是建立在一個沒被驗證過的假設上。

今日思考題

回想你最近一次讓 AI 幫你寫測試的經驗:那個測試在通過之前,有沒有真的失敗過一次?如果答案是「不知道」,代表你現在也不知道那個測試是不是能真的抓到未來的迴歸。

今日重點回顧

  • Red 階段的價值不是「測試失敗」本身,而是證明「這個測試有能力偵測到問題」
  • AI 一次性生成「實作 + 測試」的工作模式,讓測試從沒有機會經歷失敗狀態
  • 「有寫測試」不等於「測試被驗證過」,中間的落差正是假陽性測試的溫床
  • 失敗的原因要跟預期一致,才算真正驗證過測試,不能只看「有沒有變紅」
  • 具體做法:分兩步驟產出、事後把邏輯改壞驗證、把驗證動作寫進流程規則

明日預告

明天要回答一個更根本的問題:如果「先看紅燈」聽起來只是一個額外的驗證步驟,為什麼要說它比「有沒有寫測試」這件事本身更重要?


上一篇
Day 05:假陽性測試的三種樣貌——空斷言、恆真斷言、Mock 掉待測物件本身
下一篇
Day 07:為什麼「先看紅燈」比「有沒有寫測試」更重要
系列文
AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言