「我讓 AI 幫這個函式補了測試,涵蓋率也不低,怎麼上線後第一週就被一個空陣列輸入搞掛了?」
這是我接手一套系統、開始要求所有新功能都要有 AI 協作寫測試之後,最常聽到的抱怨。追查下去會發現一個規律:AI 幫忙寫的測試幾乎都精準命中「這個函式在正常情況下該怎麼運作」,卻很少主動涵蓋「如果輸入是空的、是負數、是超長字串、或是兩個請求同時打進來會怎樣」這類邊界情境。
這不是 AI 偷懶,而是AI 預設會生成的測試,幾乎都是從需求描述裡最直接能推導出來的情境——也就是 happy path。邊界案例往往不在需求描述裡明講,它們藏在「如果 XXX 沒有寫清楚會怎樣」這種隱性假設裡,而隱性假設正是 AI 最難自己補上的部分。
AI 生成測試時依賴的輸入,主要是函式簽章、文件字串、還有你給的需求描述。這些東西描述的幾乎都是「這個函式正常情況下該做什麼」——例如「輸入一個訂單,計算總金額」。這句話本身沒有告訴 AI:訂單裡的商品列表可以是空的嗎?金額可以是負數嗎?同一個訂單 ID 被重複呼叫會不會出問題?
這些邊界情境不是需求描述裡「漏講」的細節,而是需求描述這種載體本身的侷限——正常流程可以用一句話講完,例外情境卻要靠對這個領域的實際經驗才想得到。 AI 沒有那個實際踩過坑的經驗,它只能忠實反映你給它的輸入裡有的資訊,而正常情況下的資訊,永遠比邊界情況的資訊更容易被清楚地寫出來。
用一組對照來看這個差異:
❌ 只描述 happy path 的需求,AI 生成對應的測試:
「寫一個函式,計算購物車裡所有商品的總金額。」
test('calculateTotal_輸入正常商品清單_回傳正確總金額', () => {
const items = [{ price: 100, qty: 2 }, { price: 50, qty: 1 }];
expect(calculateTotal(items)).toBe(250);
});
→ 只驗證了「正常輸入下算得對」,
完全沒觸及空陣列、負數、超大數字、qty 為 0 這些情境
✅ 先明講邊界情境要求,AI 才會涵蓋:
「寫一個函式,計算購物車裡所有商品的總金額。
請先列出這個函式可能遇到的邊界情境(空輸入、極端值、
型別異常等),我確認過後再開始寫對應的測試。」
// AI 列出的邊界情境清單(人審過再動手):
// - 空陣列:總金額應為 0
// - price 或 qty 為負數:該拒絕還是視為 0?
// - qty 為 0:該計入清單還是視為無效項目?
// - 數字精度:price 是浮點數時會不會有精度誤差?
→ 邊界情境先被攤開來討論,哪些該測、該怎麼處理,
由人確認過才動手,而不是事後才發現漏了
AI 可以幫你把邊界情境「列出來」,但沒辦法幫你決定「這些情境裡哪些是真的可能發生、該怎麼處理」——這個判斷需要對這個系統實際的使用情境有理解,而這正是人該保留的角色。
與其每次都從零開始想邊界案例,不如建立一份可以重複拿來對照的分類清單,讓「要求 AI 列邊界情境」這件事變得具體、可執行:
null/undefined、字串是空字串這份清單不是要求每個函式都要測完全部類別,而是作為一份「動手寫測試前先過一輪」的檢查表——多數邊界情境其實跟這個函式的業務邏輯無關,但至少要主動篩過一次,而不是完全略過。
具體做法很直接:在要求 AI 寫測試之前,先要求它列出邊界情境清單,而且明確告訴它「先別急著寫測試程式碼」。這個中間步驟看似多花了一輪來回,但它把「AI 會不會漏測邊界情境」這個模糊的擔心,變成一個具體、可以逐條審核的清單。
清單列出來之後,人要做的判斷是:哪些情境在這個系統裡真的可能發生(例如訂單商品列表理論上不該是空的,但 API 層有沒有驗證過?)、哪些情境該怎麼處理(拋例外、回傳預設值、還是視為合法輸入)。這個判斷依賴的是對系統實際運作情境的理解,不是測試技巧本身,這正是 AI 沒辦法代勞的部分。
回想你最近一次讓 AI 幫忙寫測試的經驗:那些測試案例裡,有幾個是空值、極端值、或並發情境?如果答案是零,那份測試涵蓋率再高,可能也只是把 happy path 反覆驗證了很多遍。
第二部(Day 8-16)在明天迎來收尾:測試品質的判斷標準——AI 可以幫你寫測試,但誰來判斷這個測試值不值得留下來?