AI 生成單元測試很簡單,難的是讓測試真的有用
AI 很會寫測試,但真正困難的是讓測試值得信任。從微軟的單元測試代理,看見 AI 測試的新方向
叫 AI 產生測試,真的不難
現在只要把一段程式碼交給 AI,再補上一句:
請幫我產生單元測試。 幾秒鐘後,我們通常就能得到一份看起來很完整的測試檔案:
- 測試名稱寫得很清楚
- Arrange、Act、Assert 一個不少
- 測試可以編譯
- 執行結果全部通過
- Code Coverage 甚至還上升了
畫面看起來相當漂亮。
但真正的問題是:
這些測試真的有驗證到重要行為,還是只是看起來像測試?
測試能夠通過,不代表它具有價值。
一個測試可能只檢查結果不是 null,卻沒有確認內容是否正確;可能重複驗證同一條路徑,卻遺漏真正容易出錯的邊界情境;甚至可能測錯方法,即使正式程式永遠回傳預設值,它還是會顯示綠燈。
這也是 AI 生成測試最容易製造的錯覺:
測試數量增加了,但我們對系統的信心不一定跟著增加。
微軟想解決的不是「怎麼寫測試」
微軟近期在 .NET Blog 發表了 From generated code to trusted code with a unit-test agent,介紹開源的多語言單元測試代理 code-testing-generator。
它接受的指令甚至可以只有一句:
Generate unit tests. 不過,代理收到指令後並不會立刻開始產生測試。
它會先研究 Repository,找出待測程式碼、使用的語言與測試框架,觀察既有測試放在哪裡、命名方式與撰寫慣例,並確認專案實際使用哪些指令進行建置與測試。
這一步看似只是前置準備,實際上卻相當重要。
因為 AI 完全可以建立一個能夠獨立執行的新測試專案,讓所有測試順利通過,卻忘了把它加入 Solution 或既有的測試流程。結果本機看起來一切正常,CI 根本沒有執行到那些測試。
所以,微軟這套代理確認的不只是「測試能不能跑」,還包括:
- 測試是否放在正確的位置
- 是否符合專案原本的測試框架與慣例
- Repository 既有的測試指令能不能找到它
- 完整工作區能不能成功建置
- 完整測試套件是否仍然通過
它處理的是一整段測試工作流,而不只是產生一個測試檔案。
測試通過之後,才開始檢查測試
這套代理最有意思的地方,是它不會把「全部變成綠燈」當成任務終點。
測試完成後,它還會檢查:
- 如果對正式程式做一個小幅度的錯誤修改,測試是否應該失敗。這是一種輕量化的 Mutation Testing。
- 測試裡是否存在太弱或缺少的 Assertion。
- 使用者要求的每個情境,是否都有對應測試。
- 完整工作區與原有測試是否仍能通過。
- Repository 原本的測試指令,是否真的能找到新增的測試。
這裡真正重要的,不是哪一個模型更會寫測試,而是微軟把原本藏在資深工程師腦中的檢查流程,整理成代理必須執行的工作流。
簡單任務可以直接處理;範圍較大的任務則先研究與規劃;如果要處理整個 Solution 或達成特定覆蓋率,代理還會採用反覆執行的流程。
換句話說,決定成果的已經不只是一段 Prompt,而是:
理解專案
→ 決定範圍
→ 規劃測試
→ 產生測試
→ 執行測試
→ 檢查測試品質
→ 確認整體整合 更好的結果,不是來自更多測試
微軟公布的內部 Benchmark 很值得注意。
在 152 個來自真實 Repository 的任務中,專用代理完成了 140 個,完成率為 92.1%;使用相同模型與工具、但沒有這套 Plugin 工作流的 Copilot,則完成了 120 個,完成率為 78.9%。
更有意思的是,專用代理總共產生 6,963 個測試,比一般 Copilot 的 7,129 個還少了約 2.3%;兩者最後的平均 Line Coverage 與 Branch Coverage 卻非常接近。
也就是說,它的優勢並不是大量增加測試,或刻意把 Coverage 數字堆高,而是讓整個結果更可靠。
不過,這不代表測試品質問題已經完全解決。微軟也明確指出,當兩種方式都成功產生有效結果時,專用代理並沒有在每一項品質指標上領先;更深入的 Assertion 與 Error Path 測試,仍是後續要改善的方向。
另一組難度更高、會注入錯誤檢查測試能否抓出 Bug 的 SWE Atlas Benchmark,整體完成率也只有 36.4%。
因此,比較準確的說法不是「AI 已經能保證測試品質」,而是:
透過專門設計的工作流,我們可以大幅降低 AI 只交付一批表面上能通過、實際上沒有被完整驗證的測試。
我也曾經用子代理處理相同問題
看到這套代理時,我想到之前在公司分享 AI Workflow 時準備的一個 Mini Demo。
當時我的流程是先讓主代理盤點待補測試的程式碼,再逐步產生測試並執行型別檢查與完整測試。全部通過之後,工作還不算結束。
主代理接著會呼叫一個獨立的 test-quality-reviewer 子代理,專門審查剛產生的測試:
- 是否遺漏重要邊界情境
- Assertion 是否真的驗證了預期行為
- 是否存在重複或價值過低的案例
- 測試是不是只為了墊高 Coverage
- 在沒有原本對話脈絡的情況下,測試意圖是否仍然成立
如果子代理找到問題,主代理就會根據審查結果補測試、刪除冗餘案例,再重新執行驗證。
這個做法與微軟的方向有些相似,但角色切分不太一樣。
微軟把 Repository 研究、規劃、產生與品質檢查整合進一個專用測試代理;我的做法則是讓主代理負責產生,再交給具有獨立 Context 的子代理重新審查。
兩種設計的共同點都是:
不讓「AI 已經把測試寫完了」直接等於「這批測試可以信任」。
獨立子代理的價值也不只是多看一次。它沒有參與主代理原本的推理過程,比較不容易沿用同一組假設,更像是讓交付物接受一次獨立 Code Review。
真正該自動化的是信任流程
過去談到 AI 生成測試,我們很容易把焦點放在模型能力:
- 哪個模型比較懂 xUnit?
- 哪個模型產生的 Mock 比較正確?
- Prompt 要怎麼寫,才能涵蓋更多情境?
- 一次可以產生多少測試?
這些問題當然有價值,但它們只處理了「產生」這一步。
真正困難的部分其實發生在後面:
- 怎麼知道測試真的測對東西?
- 怎麼知道不是弱 Assertion?
- 怎麼知道 CI 找得到這些測試?
- 怎麼知道測試可以抓出程式行為被破壞?
- 怎麼避免主代理同時當球員與裁判?
微軟的單元測試代理之所以有意思,不只是因為它會寫測試,而是因為它開始把這些問題納入固定流程。
AI 生成測試很簡單。
真正值得投入心力的,是設計一套流程,讓測試在產生之後還必須被執行、挑戰、審查與重新驗證。
因為我們真正需要的,從來都不是更多綠色勾勾。
而是當那些綠色勾勾出現時,我們有足夠的理由相信它。
