如何用 AI 產生測試:先定 oracle,再以受控故障驗證

如何用 AI 產生測試:先定 oracle,再以受控故障驗證

Olivia Park
2026年8月24日· 9 分鐘讀完

要用 AI 產生測試並令它們更有用,應由需求和獨立的測試 oracle 出發,而不只看實作。先要一份正常、邊界和失敗情境的矩陣,再審查 fixture、mock、斷言和副作用,並證明每個重要測試在行為被刻意破壞時會失敗。

目標是證據,而不是更多測試數目。以在受控邊界內使用 AI的流程控制範圍,並把每個產生的測試視為必須贏得信任的程式碼。

關鍵要點

  • 產生測試前,先凍結需求和基線行為。
  • 界定甚麼可觀察的結果會令每個個案通過或失敗。
  • 要求測試程式碼之前,先建立情境矩陣。
  • 審查 fixture、mock、斷言、清理和不確定因素。
  • 以受控故障證明測試確實能偵測它聲稱保護的行為。

如何用 AI 產生測試而不是複製實作?

把公共契約、示例、限制和現有測試慣例交給模型,並要求它先提出個案、再寫程式碼。GitHub 的測試教學指出,助手可協助編寫單元和整合測試,但複雜情境需要更詳細的策略,產生的測試套件仍要檢查有否遺漏個案。[1]

較弱的提示詞是「為這個函式寫單元測試」:模型可能照搬分支、斷言私有呼叫,或重複實作中的同一誤解。較強的請求會說明哪些行為重要、哪些介面公開、預期有哪些失敗,以及甚麼必須保持兼容。

把四類產物分開:

對象回答的問題
需求承諾了甚麼行為?
Oracle怎樣判斷結果正確?
情境矩陣必須執行哪些不同條件?
測試程式碼程式庫如何執行這些檢查?

如果 oracle 只是「與目前實作一致」,測試就不可能發現實作有錯。

步驟 1:凍結需求與可信基線

以可觀察的語言寫下需求。「對負數時長傳回 InvalidDuration,而且不改變有效的秒數或分鐘」是可以測試的;「改善時長驗證」則不是。

修改前,記錄目前的測試指令和結果。修正 bug 時,保存能重現它的最小輸入。基線本已有失敗時,先把這些失敗分類,以免把無關行為的故障歸咎於新產生的測試。

程式碼陌生時,先以證據解釋目標程式碼的相關路徑。弄清入口、依賴、狀態和外部副作用後,才開始產生測試。

從實作以外定義 oracle

好的 oracle 來源包括公共 API 契約、協議規格、schema、驗收準則、之前已批准的行為,或獨立計算的結果。解析器的 oracle 可以是一份明確的輸入/輸出表;授權檢查的 oracle 可以是一份政策矩陣;財務計算則可能需要另行覆核的公式和示例。

遇到含糊之處時如實記錄,而不是讓 AI 決定產品行為。如果兩種結果都說得通,欠缺的是需要人作出的決定,而不是寫測試的問題。

步驟 2:建立正常、邊界和失敗矩陣

先要求個案名稱和理由。每列只改變一個有意義的條件,並寫明預期的可觀察結果。

至少使用以下類別:

  • 正常: 具代表性的有效輸入和兼容的輸出。
  • 邊界: 空值、零、最小值、最大值、剛好等於門檻,以及超出一步。
  • 失敗: 格式錯誤、未獲授權、依賴無法使用、逾時和被拒絕的操作。
  • 狀態: 首次呼叫、重複呼叫、重複輸入、部分既有狀態,以及失敗後重試。
  • 互動: 以正確資料呼叫依賴、拒絕時不呼叫,以及已執行清理。
  • 對抗: 相關時,注入形態的文字、過大輸入、路徑穿越或不可信文件中的指示。

不要機械式地加入類別。純格式化函式未必需要網絡故障個案,而佇列消費者更需要確認和重試行為,而非數十種字串變體。

矩陣只是規劃工具。在測試實際執行,並檢查其對故障的敏感度之前,它不代表任何涵蓋範圍。

不要以不同包裝重複同一場景

走同一分支的十個數值,通常只是一個等價類別,而不是十道獨立防線。要求模型說明每個個案能發現甚麼獨特風險;沒有帶來獨特觀察的列就刪去。

GitHub 的單元測試產生教學建議把程式碼和清晰指示交給助手,再覆核產生的結果。[2] 加入情境矩陣這一步,你便能在語法令它看似「已完成」之前評估設計。

步驟 3:提供安全且最小的測試上下文

分享需求、公共介面、相關實作、附近現有的測試、fixture 建構器和確切的測試指令。寫明程式庫有關命名、非同步行為、臨時檔案、資料庫隔離和清理的慣例。

不要貼上真實的客戶記錄、生產資料庫匯出、secret、簽署 URL 或私人憑證。建立保留結構和邊界條件的合成 fixture;逼真的 fixture 並不需要真實身份。

要求模型在第一輪不要加入依賴、大量重新產生快照、削弱斷言或修改生產程式碼。如果測試之所以難寫,是因為設計耦合過緊,就把這項設計限制另行記錄,而不是以一個龐大的 mock 掩蓋。

步驟 4:審查 fixture、mock 和斷言

產生的測試往往看似合理,卻幾乎沒有證明甚麼。按執行次序審查測試。

Fixture 必須真正製造目標條件

確認型別、單位、時區、編碼、預設設定、ID 和關聯。一個意外被規範化的 fixture,可能令邊界測試根本到不了邊界。在隱藏預設設定可能改變含義之處,讓建構器保持明確。

Mock 不應取代被測行為

有需要時 mock 緩慢或外部的邊界,但不要 mock 你正想驗證的邏輯。盡可能在公共邊界上斷言。如果 mock 了儲存層,既要驗證傳回的行為,也要驗證發給儲存層的關鍵請求。

過度規定呼叫次序的斷言,會令無害的重構失敗;約束不足的 mock,則可能放過未獲授權或重複的副作用。選擇能表達契約的互動,例如「驗證失敗後不發生寫入」,而不是斷言每一次私有輔助呼叫。

斷言必須區分合理但錯誤的輸出

result is not None、「沒有拋出例外」或籠統的快照,可能令許多錯誤輸出都通過。對真正重要的欄位、錯誤類型、狀態轉換和外部副作用下斷言。集合則要考慮次序、重複、缺漏的列和意外多出的列。

GitHub 的負責任使用材料提醒,AI 輸出可能不準確、不完整或不符意圖,需要人工監督。[3] 測試檔案與產生的生產程式碼值得同樣嚴格的審視。

步驟 5:先跑基線,再加入候選測試

加入新檔案前,先執行最接近的可信測試組。然後加入最小而連貫的一組候選測試,只執行這個範圍。失敗可能揭示真實缺陷、錯誤的 oracle、損壞的 fixture 或環境假設;不要立即修改斷言。

按以下次序排查:

  1. 重讀需求和預期觀察。
  2. 檢查 fixture,確認執行了預期的路徑。
  3. 檢查測試是否隔離而且確定。
  4. 檢查實作和保存的證據。
  5. 把含糊的產品行為交由負責人決定。

如果失敗揭示實作缺陷,就保留該個案,並轉到證據驅動的 AI 除錯流程。不要改寫測試,把 bug 描述成正確行為。

步驟 6:證明重要測試可以失敗

一個通過的測試,可能從未到達相關分支,或只斷言了一個常數。為每個高價值個案加入臨時的受控故障:反轉比較、略過驗證、傳回錯誤欄位,或省去預期的副作用。測試應因預期的原因失敗。

立即移除故障,確認測試再次通過,並檢查 diff,確保沒有殘留改動。這種本地敏感度檢查不能取代變異測試基建,但能找出空洞的斷言和無關的 fixture。

小心處理快照。快照只證明與一份已批准的檔案相同。審查語意上的改變,而不是因為產生的輸出變了就接受大規模更新。

步驟 7:檢查確定性、清理與套件配合

涉及時間、隨機性、並行、地區設定、次序或外部資源時,把候選測試執行多於一次。在契約容許時控制時鐘和隨機種子。使用臨時目錄和隔離的資料庫狀態,並確認成功和失敗時都會清理。

分層擴大驗證:

  1. 只執行新測試;
  2. 最接近的模組或套件測試;
  3. 為改動的程式碼執行 lint、型別檢查和靜態分析;
  4. 相關的整合測試;
  5. 改動準備就緒時,執行更廣泛的程式庫關卡。

不要聲稱本地通過便證明其他作業系統、生產設定、外部服務或部署也沒有問題。NIST SSDF 把測試和覆核放在一組安全開發實務之中,而不是當作唯一的完成訊號。[4]

步驟 8:把測試 patch 當作可維護程式碼審查

測試程式碼同樣適用 AI 生成程式碼 Review 清單。檢查依賴改動、隱藏的網絡存取、不安全的臨時路徑、真實憑證、過多的 fixture、緩慢的重試和過寬的權限。

問一問,日後的維護者能否快速回答三個問題:

  • 這個測試保護的是甚麼契約?
  • 甚麼改動應該令它失敗?
  • 哪些準備細節屬必要,哪些只是偶然?

優先使用能反映行為的名稱和簡短的 fixture 註解,而不是產生過程的文字紀錄。AI 對話結束後,測試和需求仍應有用。

使用 AI 產生測試後應記錄甚麼?

記錄需求、oracle、新增個案、執行過的指令、使用過的受控故障,以及未經測試的環境。把新的失敗與基線失敗分開。列出所有 mock,並說明它們取代了哪個真實邊界。

如果 AI 同時提出生產程式碼修補,就把小範圍 AI 編碼流程當作另一項授權處理。測試可以指導改動,但產生測試並不等於獲准改變它們所描述的行為。

總結

  • 由可觀察的需求和獨立的 oracle 出發。
  • 設計正常、邊界、失敗、狀態、互動及相關的對抗個案。
  • 使用最小的合成 fixture,只 mock 真正的邊界。
  • 審查斷言能否分辨正確輸出和看似合理的錯誤輸出。
  • 以受控故障證明重要測試會失敗,再分層執行驗證。

常見問題

AI 能自動產生完整測試套件嗎?

它能提出大量個案並快速寫出程式碼,但是否完整取決於需求、系統風險、環境和人的判斷。把產生的測試視為候選,而非認證。

應從實作還是需求產生測試?

兩者都用,但由需求界定甚麼才正確。實作有助找出分支和依賴,但不應成為唯一的 oracle。

覆蓋率越高越好嗎?

覆蓋率可揭示未執行的程式碼,但不能證明斷言有意義或需求正確。寧要一小組能分辨對錯的測試,也不要裝飾性的覆蓋率。

測試失敗時可以讓 AI 自動更新 snapshot 嗎?

只有在審查語意差異,並確認新輸出屬預期之後才可以。大量更新快照可能把回歸重新定義為預期,從而掩蓋它。

應該 mock 多少?

為了隔離,mock 外部或代價高的邊界,而不是被測行為。讓關鍵的輸入和輸出保持可見,並為重要的邊界假設補充整合層面的證據。

全部測試通過能證明 bug 已修復嗎?

不能。它們只證明該環境中已執行的情境。保留原本的重現,審查修補,執行相關的更廣泛檢查,並記錄仍未驗證的部分。

測試暴露需求歧義怎麼辦?

暫停,請產品、協議或政策負責人界定契約。不要讓模型在幾個合理結果之間選擇,或把偶然的行為固定為永久行為。

測試檔案可以包含生產資料嗎?

不可以。使用合成的或經正式批准的匿名化 fixture,避免憑證、客戶記錄、私人 URL 和對線上服務的依賴。


延伸閱讀:

免責聲明:本文提供一般軟件測試資訊。安全、監管、品質和發布要求取決於你的系統與機構;高影響軟件需要合資格審閱和目標環境驗證。

來源:

  1. GitHub Docs — Writing tests with GitHub Copilot — https://docs.github.com/en/copilot/tutorials/write-tests
  2. GitHub Docs — Generating unit tests — https://docs.github.com/en/copilot/tutorials/copilot-cookbook/testing-code/generate-unit-tests
  3. GitHub Docs — Application card for Copilot inline suggestions — https://docs.github.com/en/copilot/responsible-use/inline-suggestions
  4. NIST — Secure Software Development Framework — https://csrc.nist.gov/pubs/sp/800/218/final

Sources checked 2026 年 8 月 24 日。

開啟 3 天免費試用

註冊即可免費體驗全部高級功能。

*只限新用戶;每位用戶只可獲得一次試用。

如何用 AI 產生測試:先定 oracle,再以受控故障驗證 | AethoVPN