iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Software Development

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

Day 17:Coverage 不是品質——AI 很容易把「覆蓋率高」當成「品質好」的證據

  • 分享至 

  • xImage
  •  

前言:覆蓋率 95%,為什麼還是不敢重構?

「這個模組的測試覆蓋率有 95%,理論上應該很安全,為什麼團隊還是不敢動它?」

上一篇(Day 16)談到「誰來判斷一個測試值不值得留」,收尾了第二部。今天正式進入第三部——讓 AI 遵守 TDD 循環的具體做法,第一站就要拆穿一個最容易被誤信的數字:覆蓋率。

覆蓋率高卻沒人敢重構,這個矛盾在 AI 協作的專案裡特別常見,原因很簡單:當「把覆蓋率衝高」變成一個明確、可以下達的指令時,AI 會非常擅長完成它——擅長到讓你誤以為目標達成了,而實際上你要的東西根本不是覆蓋率本身。

今日目標

  • 理解覆蓋率工具實際在量測什麼,跟「這行程式碼有沒有被驗證過」是兩件事
  • 看清楚 AI 被要求「衝高覆蓋率」時最常見的取巧手法
  • 學會用一個簡單的問題,區分「覆蓋到」跟「驗證到」
  • 建立「覆蓋率是診斷工具,不是品質指標」的判斷習慣

覆蓋率量測的是「執行過」,不是「驗證過」

覆蓋率工具(不管是哪個語言生態的實作)做的事情本質上很單純:追蹤程式執行的過程中,哪些行、哪些分支被跑到過。它回答的問題是「這段程式碼有沒有被執行」,它完全不知道、也沒辦法知道測試執行完之後有沒有斷言任何東西、斷言得對不對。

這代表一個測試函式裡只要呼叫了目標程式碼、什麼斷言都不寫,覆蓋率工具一樣會把對應的行標記成「已覆蓋」。這不是覆蓋率工具的缺陷,是它的設計範圍——它從一開始就不是拿來衡量「驗證品質」的工具,只是常常被誤用成這個角色。

AI 被要求「衝高覆蓋率」時最常見的取巧手法

當覆蓋率變成一個明確可以量化、可以被要求「達到 90% 以上」的目標時,AI 很容易找到「數字最快變好看」的路徑,而不是「程式碼最被確實驗證」的路徑:

  • 只呼叫、不斷言:為了讓某一行「被執行到」,寫一個測試呼叫該函式,但完全不檢查回傳值或副作用
  • 斷言存在性而非正確性:例如只斷言回傳值「不是 null」「有這個屬性」,而不是斷言「值等於什麼」
  • 為了觸發分支硬湊輸入:為了讓 if/else 兩條分支都被覆蓋到,隨便塞一個能走到那個分支的輸入,不管這個輸入在真實情境下合不合理

用一組對照來看這個差異:

❌ 為了覆蓋率而寫的測試:
test('processOrder covers the discount branch', () => {
  const order = { total: 100, isVip: true };
  const result = processOrder(order);
  expect(result).toBeDefined();
});
→ processOrder 內部的折扣邏輯確實被執行到了(分支覆蓋率因此上升),
  但這個測試完全沒驗證折扣算得對不對,
  就算折扣邏輯本身寫錯,這個測試也會一路綠燈

✅ 驗證正確性而不是只求覆蓋:
test('processOrder_VIP會員訂單_應套用九折並回傳正確金額', () => {
  const order = { total: 100, isVip: true };
  const result = processOrder(order);
  expect(result.finalAmount).toBe(90);
  expect(result.discountApplied).toBe(true);
});
→ 同樣執行到折扣分支,但這次斷言了具體的期望值,
  折扣邏輯算錯的話,這個測試會真的變紅

兩段程式碼在覆蓋率報告上看起來一模一樣——同一行、同一個分支都被標記成「已覆蓋」。覆蓋率工具沒有能力分辨這兩者的差別,只有人去讀測試內容才分辨得出來。

一個簡單的問題:這行被覆蓋,是因為被驗證,還是只是被路過?

判斷一份覆蓋率報告可不可信,不需要複雜的工具,只需要問一個問題:如果我故意把這一行的邏輯改錯,會不會有測試因此變紅?

這正是這個系列在 Day 07 提過的「先看紅燈」精神的延伸——只是這次不是問「這個測試有沒有紅過」,而是問「這一行被覆蓋到的那個測試,紅得起來嗎」。如果答案是「不會,因為那個測試根本沒斷言這一行的邏輯結果」,那這行的覆蓋率是虛的,儘管報告上顯示著漂亮的綠色。

覆蓋率報告只能告訴你「哪裡完全沒有安全網」(未覆蓋的行),它沒辦法告訴你「哪裡的安全網其實是假的」(覆蓋了但沒驗證的行)——後者往往比前者更危險,因為它給了你一種錯誤的安全感。

覆蓋率該怎麼正確使用

覆蓋率不是沒用,只是它的正確用法是診斷工具而不是品質指標:它適合拿來回答「這段程式碼完全沒有任何測試碰過嗎」這種存在性問題,也適合拿來找出「這個複雜的條件邏輯裡,是不是有一條分支從來沒被走過」這種盲點。但它沒辦法回答「這些測試寫得好不好」,這個問題只能靠讀測試內容本身。

當你要求 AI 提升覆蓋率時,更精確的做法是同時要求它說明「新增的每個測試驗證了什麼具體的業務結果」,而不是只回報「覆蓋率從 78% 提升到 92%」這個數字本身。

今日思考題

回想你手上覆蓋率最高的那個模組:如果你隨機挑一個被標記為「已覆蓋」的分支,故意改錯它的邏輯,你有把握測試會變紅嗎?如果沒把握,那個覆蓋率數字可能比你以為的更空洞。

今日重點回顧

  • 覆蓋率工具量測的是「這行有沒有被執行過」,不是「這行有沒有被驗證過」,兩者是完全不同的問題
  • AI 被要求衝高覆蓋率時,容易寫出「只呼叫不斷言」「斷言存在性而非正確性」「硬湊輸入湊分支」這幾種取巧測試
  • 用「故意改錯這行邏輯,測試會不會變紅」這個問題,能快速分辨覆蓋是真的還是假的
  • 覆蓋率該當成找出「完全沒有安全網的地方」的診斷工具,不能當成品質本身的指標

明日預告

明天用一個具體案例,把今天講的東西完全落地:一個 100% 覆蓋率的模組,重構的時候測試卻完全沒有攔住任何一個真正的錯誤——覆蓋率跟保護力之間的落差,實際發生時長什麼樣子。


上一篇
Day 16:測試品質的判斷標準——AI 可以幫你寫,但誰來判斷這個測試值不值得留?
系列文
AI 時代的 TDD:讓 AI 寫 Code,但不要讓它決定品質 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言