iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Software Development

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

Day 09:案例——AI 為了讓測試綠燈,硬編碼了預期值

  • 分享至 

  • xImage
  •  

前言:測試綠了,是因為邏輯對了,還是因為它「記住了答案」?

「昨天講到 AI 在 Green 階段容易寫出『讓測試通過的最小手段』,聽起來有點抽象——最小手段到底能有多小?」

答案是:小到可以完全不寫邏輯,只回傳測試斷言期望的那個值。今天用一個具體案例,把這件事攤開來看——一個計算運費的函式,測試綠燈了,程式碼卻完全沒有計算任何東西。

今日目標

  • 看一個具體案例:AI 把測試斷言的期望值直接寫死進實作
  • 理解為什麼這種「最小手段」在只有一個測試案例時完全看不出問題
  • 學會用「換一組輸入」當第一道檢驗,判斷實作是不是真的有邏輯
  • 掌握這種硬編碼陷阱在 AI 協作下特別容易發生的原因

案例:一個「算出來剛剛好」的運費函式

假設現在要實作一個依訂單金額計算運費的函式:滿 1000 元免運,未滿則收 60 元運費。第一步先寫一個測試:

❌ 只有一個測試案例時,硬編碼完全不會被抓到:

function test_滿1000元訂單應該免運() {
    order = { amount: 1200 }
    expect(calculateShipping(order)).toBe(0)
}

// AI 生成的實作:
function calculateShipping(order) {
    return 0
}

測試執行,綠燈。乍看沒有問題——函式回傳了 0,測試期望的也是 0。但這段程式碼完全沒有讀取 order.amount,也沒有任何判斷邏輯。它不是「算出運費是 0」,它是「被寫死回傳 0」。只要換一筆金額低於 1000 的訂單去呼叫它,它一樣回傳 0——這個函式對任何輸入都給出同一個答案,剛好那個答案跟唯一一個測試案例期望的值一樣。

為什麼這個問題在只有一個測試時完全隱形

「測試通過」這件事本身,沒有辦法告訴你「實作是不是真的處理了輸入」——它只能告訴你「輸出符合期望」,而符合期望的方式,可以是算出來的,也可以是背下來的。 這正是 Day 09 系列主題句在單一案例層級的縮影:一個綠燈的測試,代表的查證範圍就只有它斷言過的那一組輸入,不多也不少。

當只有一個測試案例時,「硬編碼回傳固定值」跟「正確實作邏輯」在測試報告上看起來完全一樣——兩者都是綠燈,沒有任何訊號可以區分。只有補上第二個、第三個測試案例,用不同的輸入去逼函式面對不同的答案,硬編碼才會現形。

用一組對照看完整的過程:

❌ 硬編碼撐過去的版本:
function calculateShipping(order) {
    return 0
}
→ 換一筆 amount: 500 的訂單去測,
  期望運費 60 元,實際還是回傳 0 元,測試失敗
  (這時才第一次證明這個函式沒有真正的邏輯)

✅ 補上第二個測試案例後,逼出真正的實作:
function test_滿1000元訂單應該免運() {
    order = { amount: 1200 }
    expect(calculateShipping(order)).toBe(0)
}

function test_未滿1000元訂單應收60元運費() {
    order = { amount: 500 }
    expect(calculateShipping(order)).toBe(60)
}

// 這時候唯一能讓兩個測試都通過的實作,才會被逼出來:
function calculateShipping(order) {
    return order.amount >= 1000 ? 0 : 60
}
→ 兩組不同輸入、兩個不同期望值,
  硬編碼沒有辦法同時滿足兩者,只有真正判斷 amount 才行

為什麼 AI 特別容易寫出這種版本

這不是 AI 在「偷懶」,而是它在做一個合理但危險的最佳化:面對「讓這個測試通過」這個任務,最小、最直接、最不容易出錯的做法,就是回傳測試斷言的那個值本身。 對 AI 來說,這是一個完全合法的解,因為衡量標準只有「測試有沒有變綠」,沒有人明確告訴它「要真的算出來,不能背答案」。

這正是為什麼 TDD 的紀律裡,第二個測試案例的角色特別重要——它不是「多加一份保障」,而是逼一個籠統的目標(讓測試通過)收斂成一個只有真實邏輯才能滿足的具體約束。一個測試允許無限多種投機解法,兩個涵蓋不同輸入的測試,能排除掉絕大多數的投機解法。

測試綠燈只證明了『輸出符合這一組期望值』,不能證明『這段程式碼處理了你以為它在處理的邏輯』——差別要靠換一組輸入才看得出來。

今日思考題

回想你最近一次驗收 AI 寫的實作:那個函式的測試案例,涵蓋了多少組不同的輸入?如果只有一組,你有把握這個函式不是靠硬編碼撐過去的嗎?

今日重點回顧

  • 一個運費計算函式的案例:AI 把測試期望的值直接寫死,第一個測試綠燈,但完全沒有處理輸入
  • 只有一個測試案例時,硬編碼跟正確實作在測試報告上完全無法區分
  • 補上第二組、涵蓋不同輸入的測試案例,才能逼出真正的判斷邏輯
  • AI 傾向寫出「讓測試通過的最小手段」不是偷懶,是在缺乏額外限制時做的合理最佳化

明日預告

明天要看 Red-Green-Refactor 循環裡最容易被跳過的最後一步:Refactor 階段——沒有安全網的「順手重構」會踩到什麼坑。


上一篇
Day 08:Green 階段——AI 傾向寫出「讓測試通過的最小手段」
系列文
AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言