用 AI 寫單元測試:把函式貼給 AI,產出涵蓋邊界與例外的測試案例

一分鐘重點:把函式貼給 AI 叫它寫測試,三秒就能拿到一大段看起來很專業的 test code,但九成的人拿到的是「只測正常路徑、斷言又空泛」的假安全感。真正會用 AI 寫測試的人,順序是反過來的:先逼 AI 把正常、邊界、例外三類輸入「列成清單」,自己審過、補上業務規則,再讓它寫程式碼,最後一條一條審斷言。這篇給你完整流程、可複製 Prompt,以及怎麼看穿 AI 產的爛測試。

單元測試是少數「AI 幫得上大忙、但你不審就會反咬一口」的任務。寫對了,補測試的時間從一個下午縮到半小時;用錯了,你會得到一整套永遠綠燈、卻什麼 bug 都抓不到的測試,反而讓整個團隊更敢亂改程式碼。差別不在模型多強,在你怎麼指揮它。

為什麼直接叫 AI「幫這個函式寫測試」會出事

先看最常見的用法。你把一個函式貼進去,打一句「幫我寫單元測試」,AI 唰一下給你五六個 test case,全綠,覆蓋率還 92%。看起來很美好,問題藏在三個地方。

第一,它幾乎只測 happy path。給合法輸入、回傳合理結果,這種案例 AI 寫起來最順,因為從函式簽章就猜得到。但 bug 很少躲在正常路徑,都躲在「空陣列」「數量是 0」「日期跨年」「金額剛好等於門檻」這些邊界,還有「傳進來是 null」「外部 API 掛掉」這些例外。AI 不會主動幫你想這些,除非你逼它。

第二,斷言空泛。你會常看到 expect(result).toBeTruthy()expect(result).toBeDefined()assert result is not None。這種斷言的意思是「有東西回來就算過」,等於沒測。一個算錯金額的函式只要它有回傳數字,這種斷言照樣綠燈。

第三,也是最危險的:測試跟著實作一起錯。AI 寫補測試時,是「讀你的程式碼、反推它應該回傳什麼」。如果你的函式本來就有 bug,算出來是錯的,AI 會把這個錯誤的輸出當成「正確答案」寫進 expect(...).toBe(錯誤值)。結果就是——測試永遠通過,bug 永遠在,而且你還因為「有測試」而更放心。這是用 AI 補測試最容易踩、又最難自己發現的坑。

理解這三件事,後面的流程就是針對它們設計的。

正確流程:先列案例清單,再寫測試

老手請 AI 寫測試,會刻意拆成兩個回合,中間插一個「人」。

第一回合,只要清單,不要程式碼。 把函式貼上去,要求 AI 先用條列方式列出它打算測哪些輸入,分成「正常 / 邊界 / 例外」三類,每一條寫清楚「輸入是什麼、預期結果是什麼、為什麼要測」。這一步只產生一張表,不產生任何 test code。

為什麼要這樣?因為清單是「人類最容易審」的形式。你掃過去馬上看得出來:喔,它沒測空陣列、沒測負數、沒考慮到我們業務規定「未滿 18 歲要擋下來」。這時候你直接在清單上補:「加上 age 剛好 18、剛好 17 的案例」「補上 list 為空時應該回傳 0 而不是報錯」。把業務規則灌進去——這正是 AI 最不懂、最需要你的地方。

第二回合,才讓它把確認過的清單寫成測試程式碼。 這時候 AI 是照著你審核過的規格寫,而不是照著可能有 bug 的實作猜,測試的品質直接差一個檔次。

這個「先清單後程式碼」的動作,就是在把人塞進 AI 的決策鏈裡。多花三分鐘審清單,省下的是事後 debug 一整套假測試的時間。

可複製 Prompt 範本

下面這份範本就是上面流程的具體化。把你的函式貼進去,照著用:

你是一位資深測試工程師,擅長寫嚴謹的單元測試。

【任務】
我會貼一個函式,請你協助我寫單元測試。
但「先不要寫程式碼」,分兩階段進行。

【第一階段:列測試案例清單】
請先用表格列出你打算測的所有案例,分成三類:
1. 正常路徑(typical inputs)
2. 邊界條件(空值、0、空字串、空陣列、最大/最小、剛好等於臨界值、邊界±1)
3. 例外情況(null/undefined、型別錯誤、超出範圍、重複、外部依賴失敗或丟錯)

每一列請寫清楚:輸入 / 預期結果 / 這條在驗什麼。
重點:預期結果請依「需求規格與常理」推導,
不要只是照我貼的實作反推——如果你覺得實作的行為可能有問題,請標記出來提醒我。

【第二階段:等我確認清單後,再寫測試程式碼】
- 測試框架:<填 Jest / Vitest / pytest / JUnit…>
- 每條測試要有清楚的測試名稱,說明在驗什麼情境。
- 斷言要驗到「具體的值、長度、欄位內容或丟出的錯誤型別」,
  禁止使用 toBeTruthy / toBeDefined / 只判斷有無回傳這類空泛斷言。
- 例外案例要用框架的方式驗證有正確丟錯(例如 expect(...).toThrow)。

【我的函式】
<把完整函式、它依賴的型別/常數、以及它在系統裡負責什麼,一起貼在這>

先做第一階段,把案例清單給我,我審完會回覆你補充的業務規則。

關鍵在三件事:強迫分兩階段、明文要求三類案例、明文禁止空泛斷言。把這三條寫死在 Prompt 裡,你會發現 AI 產出的測試品質穩定很多,不用每次靠運氣。

一段實際的對照

假設你有一個算運費的函式(簡化版):

// 訂單金額滿 1000 免運,否則運費 80;金額需為正數
function shippingFee(amount) {
  if (amount >= 1000) return 0;
  return 80;
}

新手 Prompt 拿到的測試通常長這樣:

test('計算運費', () => {
  expect(shippingFee(1200)).toBeTruthy();   // 1200 回傳 0,0 是 falsy,這條其實會掛
  expect(shippingFee(500)).toBe(80);
});

只測了兩個正常值,斷言還寫錯(0 是 falsy)。而走「先列清單」流程,你會被提醒到這些案例:amount 剛好等於 1000(臨界值,該免運)、999(差一塊,要收 80)、0、負數、傳進字串或 null。補完之後:

test('滿 1000 免運', () => expect(shippingFee(1000)).toBe(0));
test('999 仍需運費', () => expect(shippingFee(999)).toBe(80));
test('金額為 0 收運費', () => expect(shippingFee(0)).toBe(80));
test('負數金額應拋錯', () => expect(() => shippingFee(-50)).toThrow());

寫到第四條你會發現——原函式根本沒處理負數!測試逼出了實作的漏洞。這就是好測試的價值:它不是替現狀背書,而是替「正確的行為」設防線。順帶一提,這也示範了為什麼「照實作反推」很危險:如果你只叫 AI 看實作寫測試,它根本不會質疑負數,因為原函式沒擋,它就以為負數回 80 是對的。

台灣團隊實戰:一句「先列清單」救回的信心

台中一家做 SaaS 出貨管理的新創 Shipfast,後端團隊四個人,主力是工程師林冠廷。他們的計費模組累積了兩年,沒幾條測試,每次改報價邏輯都像拆地雷,改完只能手動點一輪,沒人敢大改。

第一次他們試著用 AI 補測試,是直接把整個 billing.js 丟給 Cursor,叫它「補完整測試」。產出兩百多行、覆蓋率衝到 88%,看起來很安心。直到一週後一個客戶反映「滿額折扣沒生效」,他們回頭一看——那條折扣邏輯本來就寫錯了,而 AI 補的測試是照著錯的實作寫的,expect 裡填的正是錯誤金額,所以一路全綠,沒人發現。

林冠廷後來改了做法。他不再讓 AI 一口氣寫,而是改用「先列清單」的兩階段流程,並且在 Prompt 裡加一句關鍵指令:「預期金額請依我們的計費規格推導,不要照現有程式碼反推,覺得實作可疑就標記。」第二次跑下來,AI 在清單階段就標了三個「這裡的行為跟一般折扣邏輯不一致,請確認」,其中兩個正是真 bug。重要的金額案例,他要求團隊自己用計算機對一遍答案,再寫進斷言。

三週後的結果:計費模組從 9 條測試補到 140 多條,更重要的是抓出 4 個潛藏 bug。林冠廷的說法是:「以前覆蓋率數字好看但心裡沒底,現在條數沒灌水、每條斷言都對過答案,改報價邏輯終於敢一次改一大塊。」他們現在團隊規範寫死一條:AI 產的測試,清單一定要人審、關鍵斷言一定要手算對答案,才能 merge。

怎麼審查 AI 產的測試品質

測試寫出來只是一半,會審才是真功夫。逐條過這份檢查清單:

這份清單花不了五分鐘,但它是 AI 產測試「能不能信」的分水嶺。

常見錯誤與 AI 的限制

把容易踩的雷集中講一次:

測試跟著實作一起錯——前面講過,最隱蔽。解法是叫 AI 依規格推導、自己手算關鍵答案、用 mutation 驗收。

只測 happy path——AI 的預設傾向。解法是 Prompt 裡強制三類案例、先審清單再寫。

斷言空泛——衝覆蓋率最容易產生的副作用。解法是明文禁用空泛斷言、審查時逐條看。

過度信任覆蓋率數字——覆蓋率只說「這行有被跑到」,不說「行為有被驗對」。空泛斷言可以讓覆蓋率很漂亮卻零防護力。把覆蓋率當「哪裡完全沒碰到」的地圖就好,別當品質指標。

至於 AI 本身的限制,要認清三點。其一,AI 不懂你的業務規則。「未滿 18 不能下單」「VIP 免運門檻是 500 不是 1000」「跨年訂單算前一年業績」——這些它無從得知,只能靠你在清單階段補。其二,AI 不知道你系統的真實依賴行為,它對外部 API、資料庫、時間的 mock 常常想當然耳,要你校正。其三,AI 產的測試一定要人工審,它是把你從「從零寫一百條」加速到「審一百條、修二十條」,不是讓你從此不用看。把它當一個手很快但不懂你產品的實習生用,剛剛好。

結語:今天就挑一個函式試一次

不用大張旗鼓導入。打開你專案裡那個「最沒人敢動、又沒測試」的函式,把上面的可複製 Prompt 套上去,走一次兩階段流程:先看 AI 列的案例清單、補上你的業務規則、再讓它寫程式碼、最後逐條審斷言、故意改壞跑一次看會不會紅。一個函式做完,你就會抓到手感,知道哪些該補、哪些 AI 在唬你。

把這個流程變成團隊規範:AI 產的測試,清單要人審、關鍵斷言要對答案、要能擋住 mutation 才准 merge。這樣補出來的測試才是真的防線,而不是讓大家更敢亂改的假安全感。現在就挑一個函式,開始第一回合。


本文由 AgentAI 智庫(agentai.tw) 整理撰寫,台灣最大的繁體中文 AI 知識站。

常見問題 FAQ

把函式貼給 AI,它就能寫出好的單元測試嗎?
能寫出『一堆』測試,但不一定是『好』測試。AI 很會覆蓋正常路徑,卻常漏掉邊界與例外,斷言也容易寫得空泛。關鍵是先逼它列出測試案例清單、你補上業務規則後再讓它寫程式碼,並且每一條斷言都要人工審過,不能照單全收。
為什麼 AI 產的測試常常跟著實作一起錯?
因為 AI 是讀你的實作來反推『預期值』。如果函式本身算錯,它會把錯誤的輸出當成正確答案寫進斷言,測試自然永遠綠燈。破解方法是叫 AI 根據『需求規格』而不是『現有實作』推導期望值,重要案例自己動手算一遍對答案。
怎麼讓 AI 不要只測 happy path?
在 Prompt 裡明確要求它分三類列案例:正常輸入、邊界條件(空值、零、最大最小、剛好臨界)、例外情況(型別錯誤、null、超出範圍、外部依賴失敗)。先看清單再寫測試,缺哪類就點名補,比一句『寫完整一點』有效得多。
AI 寫的斷言太空泛怎麼辦?
像 expect(result).toBeTruthy() 這種等於沒測。請在 Prompt 規定『斷言必須驗到具體的值、長度、欄位內容或錯誤型別』,並要求每條測試都有清楚的測試名稱說明在驗什麼。審查時看到模糊斷言就退回重寫。
需要先寫好測試再請 AI,還是先有實作再補測試?
兩種都可以用 AI。如果走 TDD,可以先把規格給 AI 產測試、再寫實作;如果是替既有程式碼補測試,就把函式貼上去產測試。後者要特別小心測試跟著實作錯,前者則要小心 AI 把規格理解偏了,兩種都得人工審。
覆蓋率 100% 是不是就代表測試夠好?
不是。覆蓋率只說明每行程式碼有被執行到,不代表行為被驗證對。斷言空泛照樣能衝高覆蓋率。把覆蓋率當『哪裡完全沒測到』的提醒就好,真正該看的是斷言有沒有抓到該抓的錯。

延伸閱讀

幫這篇打個分:
A
AgentAI 智庫團隊 ✓ 台灣實作團隊

我們是一群專注於 AI Agent、Prompt 與自動化工作流的台灣實作者。每篇教學都附可複製配方、誠實標示實測程度與限制,只分享真正能落地、可直接套用的方法——與其介紹工具,不如教你把事情做完。

關於我們 →看更多教學 →訂閱情報週報 →

每週把這類實戰教學寄給你

訂閱 AgentAI 智庫情報週報,新的 Prompt、AI Skills、工作流與教學第一時間收到。

免費 · 隨時取消