iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
Software Development

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

Day 05:假陽性測試的三種樣貌——空斷言、恆真斷言、Mock 掉待測物件本身

  • 分享至 

  • xImage
  •  

前言:綠燈不等於「測到了」

「這個測試是綠的,代表這段邏輯是對的吧?」

Day 04 講過一個具體案例:AI 生成的測試全綠,卻沒測到任何有意義的行為。今天要把這件事系統化——假陽性測試不是單一一種毛病,而是三種不同的病灶,症狀都一樣(綠燈),但成因、辨識方法、修法完全不同。分不清楚是哪一種,就只能繼續靠直覺猜,猜不準。

今日目標

  • 認識假陽性測試的三種樣貌,各自的辨識特徵
  • 學會用「如果我故意把實作寫壞,這個測試會不會變紅」當通用檢驗法
  • 理解為什麼 AI 特別容易寫出這三種假陽性,而不是隨機分布在各種錯誤裡
  • 建立一套快速掃描既有測試、抓出假陽性的檢查習慣

樣貌一:空斷言/斷言太弱

最容易發現,也最容易被忽略的一種——測試裡確實有 assert,但斷言的內容弱到幾乎驗證不了任何事。

❌ 空斷言/斷言太弱:
result = calculateDiscount(order)
assert result is not null
→ 不管 calculateDiscount 算出多少、算法對不對,
  只要沒有直接爆炸、回傳值不是 null,這個測試永遠綠燈

✅ 斷言具體的期望值:
result = calculateDiscount(order)
assert result == 150
→ 如果計算邏輯改壞了,算出的數字跟預期不同,
  測試會立刻變紅

辨識方法很簡單:故意把待測邏輯改壞(例如把加法改成減法),如果測試還是綠燈,就代表斷言太弱。 這個檢驗法本身就是一種輕量級的「突變測試(mutation testing)」思維——不用真的跑突變測試工具,光是手動改壞一次、重跑一次測試,就能抓出這類問題。

樣貌二:恆真斷言

比空斷言更隱蔽,因為表面上看起來斷言了具體的東西,但邏輯上這個斷言不可能為假。

❌ 恆真斷言:
result = validateAge(age)
assert result == true || result == false
→ 這個斷言邏輯上永遠成立,因為布林值不是 true 就是 false,
  不管 validateAge 的邏輯對不對,這個斷言都通過

❌ 另一種常見樣貌:
items = getFilteredItems(criteria)
assert items.length >= 0
→ 陣列長度本來就不可能是負數,這個斷言永遠為真

恆真斷言之所以容易被 AI 寫出來,是因為它「看起來」符合一個測試該有的結構——有 Arrange、有 Act、有 Assert,語法完全正確,甚至斷言的變數名稱聽起來跟業務邏輯有關。但只看語法結構沒辦法分辨這件事,要真的讀懂斷言的邏輯內容,判斷它有沒有可能為假。

樣貌三:Mock 掉待測物件本身

三種裡最危險的一種,因為它連「斷言」本身都可能寫得很扎實——問題出在斷言驗證的根本不是真實邏輯,而是被 mock 掉的行為。

❌ Mock 掉待測物件本身:
mockCalculator = mock(PriceCalculator)
mockCalculator.calculate(returns: 150)
service = OrderService(mockCalculator)

result = service.processOrder(order)

assert result.total == 150
→ 這個測試表面上在測 OrderService.processOrder,
  但 150 這個數字是 mock 設定出來的,不是真的算出來的。
  PriceCalculator 裡的計算邏輯不管改成什麼,這個測試都不會變

✅ 只 mock 真正需要隔離的外部依賴,核心邏輯留給真實物件驗證:
realCalculator = PriceCalculator()
service = OrderService(realCalculator)

result = service.processOrder(order)

assert result.total == 150
→ 150 是 PriceCalculator 真的算出來的,
  計算邏輯改壞了,這個測試會反映出來

判斷該不該 mock 的關鍵問題是:「這個依賴是不是這次測試真正想驗證的核心邏輯」。 如果答案是「是」,就不該被 mock 掉;如果答案是「不是」(例如外部 API、資料庫、檔案系統),mock 掉才是正確做法。AI 容易搞混的地方是,它傾向對「所有依賴」一視同仁地 mock,而不會先問這個問題。

為什麼 AI 特別容易寫出這三種假陽性

這三種樣貌有一個共同點:它們都能讓測試「看起來像一個測試」,語法上無可挑剔,執行結果是綠燈——這正是 AI 最容易被獎勵訊號誤導的地方。 如果評判「這個測試寫得好不好」的訊號只有「有沒有 assert」跟「有沒有通過」,AI 自然會往「用最省力的方式讓這兩個條件成立」收斂,而不是往「這個測試有沒有真的驗證到業務邏輯」收斂。

這也解釋了為什麼空斷言、恆真斷言、過度 mock 這三種假陽性,不是隨機出現的錯誤,而是同一個誘因(優化錯誤的訊號)在不同技術情境下的三種具體結果:空斷言是「懶得寫出具體期望值」、恆真斷言是「寫了看起來像斷言但邏輯上沒意義的東西」、過度 mock 是「隔離到連要測的東西本身都被隔離掉了」。

一個通用的檢驗習慣

不管遇到哪一種假陽性,有一個共通的檢驗方法:拿到一段測試,先問「如果我故意把待測邏輯改壞(改錯運算、改反條件判斷、拿掉一段處理),這個測試會不會變紅」。 如果答案是「不會」,這個測試就是假陽性,不管它屬於三種樣貌裡的哪一種。

這個檢驗習慣值得養成,因為它比「讀懂每一行程式碼判斷寫得對不對」更省力,也更可靠——不用理解整段業務邏輯,只需要做一次思想實驗(或真的手動改壞一次),就能快速篩出可疑的測試。

今日思考題

回想你手上最近寫的(或 AI 幫你寫的)幾個測試,挑一個出來做今天的檢驗:故意把待測邏輯改壞,這個測試真的會變紅嗎?如果沒把握,那個測試的可信度,其實比你想的還低。

今日重點回顧

  • 假陽性測試有三種不同樣貌:空斷言/斷言太弱、恆真斷言、Mock 掉待測物件本身,症狀相同(綠燈)但成因不同
  • 通用辨識法:故意把待測邏輯改壞,測試沒有變紅就是假陽性
  • 該不該 mock 的判斷標準:這個依賴是不是這次測試真正想驗證的核心邏輯
  • 三種假陽性的共同根源:AI 傾向優化「看起來像測試、能通過」這個表面訊號,而不是「真的驗證了業務邏輯」

明日預告

明天要進入 Red 階段:AI 在寫測試時,最容易跳過的一步不是寫測試本身,而是「先讓測試真的失敗過一次,確認它真的會抓到問題」——這一步被跳過時,前面講的三種假陽性,其實有一大半根本不會被發現。


上一篇
Day 04:案例——AI 生成的測試全綠,卻沒測到任何有意義的行為
系列文
AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言