crucible 是做什麼的?
使用時機
當使用者要求撰寫、修復或審計 TypeScript 專案的單元測試時使用。
不應使用的情況
當撰寫端對端測試、整合測試、UI 測試或模擬測試時,請勿使用。
專案層級選擇退出
啟用前,請檢查專案是否已選擇退出此技能。尋找以下任一標記:
- 專案根目錄中存在
.no-crucible檔案 - 在
AGENTS.md或.claude/settings.json中存在"crucible: false"
如果找到任何選擇退出訊號,請勿自動啟用;僅在使用者在此對話中明確要求時才套用此技能。
規則
- 範例指令使用
pnpm,但一律使用專案的套件管理器。 - 除非使用者特別要求,否則請勿修改應用程式碼。
- 當要求新增測試時,請勿修改現有測試。
- 當要求修復測試時,請勿新增測試。
- 當要求審計測試時,請先修復現有測試,然後再根據測試標準新增所需的測試。
測試標準
單元測試應:
- 達到 Vitest 所能允許的最大分支覆蓋率。
- 消滅所有可殺死的突變體,達到提示範圍內所有程式碼(不限於測試涵蓋的程式碼)的最高可能突變分數。無法殺死的突變體將導致無法達到 100 分。
- 盡可能使用基於屬性的測試,除非只有少數輸入案例。例如,一個只有一個布林參數且沒有隱含輸入的函數,只有兩種可能的輸入案例,因此不需使用屬性測試。
- (如果是屬性測試)其 arbitraries 應涵蓋所有可能的參數和隱含輸入。確保不遺漏任何可能在生產環境中出現的案例。必要時或不清楚時,寧可讓 arbitraries 過於寬鬆。
- 如果測試的程式碼有誤,或 arbitraries 故意設定得過於寬鬆,測試應該失敗。將測試視為需求:它們應根據需求命名,並在使用者未來修正函數和/或 arbitraries 中的錯誤後自動通過。請勿使用
test.fails/.skip,這會降低使用者對問題的注意;因此,測試在不該通過時通過是最需避免的高優先級錯誤。
檢查分支覆蓋率並執行測試:pnpm exec vitest run src/foo.test.ts --coverage
檢查被測試程式碼中存活的突變體:pnpm exec stryker run --mutate "src/foo.ts"
驗證測試環境
- 確認專案已安裝以下所有套件:
vitest fast-check @stryker-mutator/core @stryker-mutator/vitest-runner @stryker-mutator/typescript-checker - 確認 Vitest 和 Stryker 的組態正確包含/排除檔案。Stryker 不應檢查僅包含測試資料的檔案。
- 確認 Stryker 組態:
- 使用
clear-textreporter(其他 reporter 可選)。 - 使用 TypeScript checker 並正確指向 TS 組態檔案。
- 使用
- 檢查是否有其他可能影響測試撰寫流程的組態問題。
- 將發現結果回報給使用者。
撰寫測試
當要求撰寫新測試時,請勿修改應用程式碼或現有測試。
首先,按照評估模組的步驟評估現有測試。使用範本建立新的簡報檔案,並在其中記錄初始覆蓋率和突變分數。
其次,嚴格遵循以下流程撰寫測試:
對模組介面中的每個函數/方法執行以下步驟,完成一個函數後再繼續下一個:
- 透過檢查註解、函數名稱、型別簽章以及使用方式,確定函數的用途。如果不確定,請在簡報中記錄此情況,跳過撰寫測試,繼續處理下一個函數。
- 記錄函數實作中可能存在的錯誤。
- 根據函數用途,找出函數對其參數和隱含輸入所做的錯誤假設,並記錄可能導致函數行為不正確的輸入案例。例如,一個接受正整數但未驗證輸入的函數,就是對其輸入做出了錯誤假設。
- 切勿修改既有測試。如果在現有測試中發現任何問題,請通知使用者。
- 透過以下步驟撰寫新測試:
a. 使用
fast-check建立基於屬性的測試樁,arbitraries 應涵蓋函數的所有可能參數和隱含輸入。當被測試函數是非同步時,使用fc.asyncProperty+await fc.assert(...)。如有疑慮,寧可讓 arbitraries 過於寬鬆,並在簡報中註明,以便使用者修正。將測試命名為對預期行為的描述。 b. 使用vitest在樁內撰寫斷言,涵蓋任何回傳值和副作用,例如拋出例外、變異輸入、變異/重新指派外部作用域的變數等。確保測試斷言的是預期行為,並能捕捉先前識別出的錯誤假設/錯誤,從而正確地失敗。 - 重新檢查覆蓋率:
pnpm exec vitest run src/foo.test.ts --coverage - 僅突變測試當前函數:
pnpm exec stryker run --mutate "src/foo.ts:<startLine>-<endLine>" - 修改新撰寫的測試,並重複步驟 5a 和 5b,直到函數滿足測試標準(如上所列)。
- 在簡報中為此函數新增一個章節。
為所有函數撰寫完測試後,請再次確認已為每個函數新增了簡報章節,並完成摘要部分的撰寫。
審計測試
如果要求審計整個專案,請檢查專案範圍的程式碼覆蓋率和突變分數,並建議需要更詳細審計或新增/更新測試的區域。建議應優先關注程式碼庫中最重要且最容易出錯的區域——核心業務邏輯、事件驅動程式碼以及複雜函數。
如果要求審計專案的某個部分,請找出提示範圍內沒有測試的模組或函數,並在審計後簡報中向使用者報告。
對於提示範圍內有測試的每個模組,請按照評估模組的步驟進行。接著,如果在評估修正後仍有未滿足的測試標準,請按照撰寫測試的步驟填補其餘差距。
評估模組
無論是在撰寫新測試前還是審計時,每當需要根據測試標準評估現有測試檔案時,請使用此流程:
- 使用 Vitest 檢查程式碼覆蓋率:
pnpm exec vitest run src/foo.test.ts --coverage。 - 使用 Stryker Mutator 檢查存活的突變體:
pnpm exec stryker run --mutate "src/foo.ts"。 - 對於模組中已有測試的每個函數:
- 針對基於屬性的測試:
- 尋找函數會接受(不會拋出例外或回傳錯誤值)但 arbitraries 無法產生的輸入。根據需要擴大 arbitraries 以包含此類輸入。
- 尋找 arbitraries 未涵蓋的輸入,尤其是隱含輸入,例如類別欄位、全域變數、日期/時間和隨機產生的數字。為這些輸入新增 arbitraries。
- 修正模擬實作中的任何錯誤。
- 尋找效能瓶頸。
- 針對基於屬性的測試:
- 將覆蓋率、突變分數以及任何發現記錄在簡報中。
測試簡報
按照以下範本格式撰寫簡報。如果函數子章節中沒有需要報告的內容,請直接寫「無」。將簡報輸出到 debriefs/ 目錄下的 Markdown 檔案中。
簡報:{moduleName/fileName} {yyyy-mm-dd}
摘要
覆蓋率(分支) 之前:X% → 之後:Y% 無法覆蓋的行數:{行數範圍}
突變分數(總計) 之前:X% → 之後:Y%
問題
{錯誤假設、發現的錯誤、測試中發現的問題以及失敗測試的項目符號清單。每個項目應以發現問題的檔案相對於專案根目錄的路徑和行號開頭}
{functionName}
函數用途 {一句話描述函數應該要做什麼} (信心程度:高 | 不確定) {如果不確定:不清楚的內容及其原因}
發現的錯誤假設
- (第 {行號/範圍} 行):{函數未驗證的輸入型別/範圍/形狀假設}
實作中發現的錯誤
- (第 {行號/範圍} 行):{描述 + 行號}
既有測試中發現的問題
- (第 {行號/範圍} 行):{問題描述 + 測試名稱}
撰寫的測試案例
- {每個測試的屬性或不變量的簡短描述}
失敗的測試
- (第 {行號/範圍} 行):{測試名稱 + 原因}
無法殺死的突變體
- (第 {行號/範圍} 行):{簡短描述}