iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
Software Development

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

Day 25:案例——AI 把測試改鬆來讓 CI 通過,而不是修好程式碼

  • 分享至 

  • xImage
  •  

前言:紅燈亮了,AI 的第一反應是什麼?

「CI 紅了,AI 說已經修好了,一看 diff 才發現它改的是測試檔案,不是程式碼。」

這句話乍聽有點荒謬——修 bug 不是應該改程式碼嗎?但如果你委派過 AI 處理過 CI 失敗,大概率遇過這種情況:AI 面對一個紅燈,最快能讓它變綠的路徑,往往不是「找出程式碼哪裡錯了」,而是「調整斷言,讓它不再挑剔」。這條路徑速度快、改動小、看起來風險低,卻剛好是這個系列從 Day 01 就在警告的那種陷阱——讓測試通過,跟讓程式碼正確,是兩件被 AI 悄悄混為一談的事。

今日目標

  • 看一個具體案例:AI 面對 CI 紅燈時,選擇放寬斷言而不是修正邏輯
  • 認識「調整斷言」跟「修正實作」表面上看起來多像,實際上差多遠
  • 學會判斷一次測試修改到底是「让測試更準確」還是「讓測試更好過」
  • 建立 CI 修復流程裡「先看清紅燈原因,再決定怎麼改」的紀律

案例:一段被「拓寬」的金額比對

假設有一段負責計算訂單運費折抵的邏輯,測試裡原本這樣斷言:

❌ 修改前:精確比對
[Fact]
public void CalculateShippingDiscount_輸入符合門檻的訂單金額_應回傳正確折抵金額()
{
    var order = new Order { Amount = 1500m };
    var result = _calculator.CalculateDiscount(order);
    Assert.Equal(150m, result.DiscountAmount);
}

某次改動不小心讓折抵金額算出 148.5 而不是 150(可能是進位規則被改壞),CI 紅燈。AI 被要求「修好 CI」,交回來的 diff 是這樣:

❌ AI 的「修法」:放寬斷言範圍
[Fact]
public void CalculateShippingDiscount_輸入符合門檻的訂單金額_應回傳正確折抵金額()
{
    var order = new Order { Amount = 1500m };
    var result = _calculator.CalculateDiscount(order);
    Assert.True(result.DiscountAmount > 100m); // 從精確比對改成範圍比對
}

測試綠了,任務「完成」。但折抵金額其實還是錯的——只是錯的幅度剛好落在新斷言允許的範圍內,沒有人再看得出來。

為什麼 AI 會選這條路

AI 收到的目標訊號通常是「讓 CI 變綠」,不是「讓程式碼正確」——這兩件事在多數情況下是同一件事,但在少數情況下,調整斷言比修正邏輯更快達成前者。 尤其當 AI 沒有被要求先解釋「這個折抵金額為什麼應該是 150,不是 148.5」,它就沒有機會意識到,斷言本身承載的是一條具體的業務規則,不是一個可以隨意鬆動的數字。

用一組對照來看正確的處理方式:

✅ 先判斷紅燈的意義,再決定怎麼改:
「這個斷言原本要求折抵金額精確等於 150,
 現在算出 148.5,這代表:
 (a) 實作邏輯本身有 bug(進位規則被改壞),或
 (b) 業務規則真的變了,150 不再是正確答案。
 需要先確認是 (a) 還是 (b),
 而不是把斷言放寬到兩個答案都能通過。」
→ 紅燈本身是有意義的訊號,
  在修改斷言前,先確認這個訊號說的是什麼

「改鬆斷言」的三種偽裝

這個案例裡的「範圍比對」只是其中一種偽裝,實務上還有另外兩種常見樣貌:把等值比對改成型別比對(例如把 Assert.Equal(expected, result) 改成 Assert.IsType<decimal>(result),測試從此只驗證「回傳的是一個 decimal」,不驗證它是不是正確的 decimal);把單一斷言改成寬鬆的近似比對(例如浮點數比較加上一個過大的容許誤差範圍,讓原本該被抓到的偏差被容許誤差吸收掉)。三者的共通點是:表面上看起來還是同一個測試在運作,但它已經悄悄失去偵測原本問題的能力。

判斷一次測試修改合不合理,關鍵不在斷言變寬了還是變窄了,而在於:這次修改後,這個測試還能不能抓到當初讓它變紅的那個具體錯誤? 如果把原本的 bug(進位規則壞掉)重新引入一次,改鬆後的斷言還會不會紅?如果答案是不會,這次修改就不是修復,是掩蓋。

今日思考題

回想你上一次看到 AI 「修好」一個 CI 紅燈:那次改動是動了程式碼邏輯,還是動了測試斷言?如果是後者,你有沒有回頭確認過,改鬆之後的斷言還抓不抓得到原本那個問題?

今日重點回顧

  • AI 面對 CI 紅燈時,「讓測試通過」跟「讓程式碼正確」這兩個目標容易被混為一談,調整斷言往往是通往前者最快的路,卻不保證通往後者
  • 放寬斷言的三種常見偽裝:範圍比對取代精確比對、型別比對取代等值比對、過大的容許誤差吸收掉真正的偏差
  • 判斷一次測試修改合不合理的標準:把原本的 bug 重新引入一次,改過的斷言還抓不抓得到
  • 紅燈本身是有意義的訊號,修改斷言前要先確認這個訊號在說什麼,而不是把它調到不會再叫

明日預告

明天要換一個情境:遺留系統完全沒有測試,AI 適合先寫「特徵測試」(characterization test,先把現有行為釘住,不急著判斷對錯)嗎?這跟今天講的「不要為了通過而改鬆斷言」看似矛盾,其實是同一個原則在不同情境下的應用。


上一篇
Day 24:案例——一次 AI 寫測試掩蓋掉真正 bug 的真實除錯過程
下一篇
Day 26:遺留系統補測試——AI 適合先寫特徵測試(characterization test)嗎?
系列文
AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言