Communitygithub.com

gpanakkal/crucible

Agent技能,用於編寫TypeScript單元測試,確保測試在程式碼有問題時正確失敗。

crucible 是什麼?

Crucible 是一個專為 AI 代理(如 Claude Code、Cursor 等)設計的技能,用於在 TypeScript 專案中編寫、修復或審計單元測試。其核心理念是「測試應該在程式碼出錯時失敗」,因此它要求測試必須達到最高的分支覆蓋率和突變測試分數,並鼓勵使用基於屬性(property-based)的測試方式。該技能支援 Vitest 和 Stryker Mutator 工具,會自動檢查專案是否已選擇退出(透過 .no-crucible 檔案或設定檔),並在啟動前驗證測試環境(安裝套件、組態正確性)。使用時,它會先評估現有測試並記錄基線數據,然後逐一為每個函數撰寫嚴格的測試案例,並反覆驗證直到滿足覆蓋率和突變分數標準。Crucible 特別強調不修改應用程式碼或既有測試,並要求測試命名反映預期行為,避免使用 test.fails 或 test.skip,以確保測試失敗能清楚提示使用者需要修正的錯誤。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add gpanakkal/crucible

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

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"

驗證測試環境

  1. 確認專案已安裝以下所有套件:vitest fast-check @stryker-mutator/core @stryker-mutator/vitest-runner @stryker-mutator/typescript-checker
  2. 確認 Vitest 和 Stryker 的組態正確包含/排除檔案。Stryker 不應檢查僅包含測試資料的檔案。
  3. 確認 Stryker 組態:
    1. 使用 clear-text reporter(其他 reporter 可選)。
    2. 使用 TypeScript checker 並正確指向 TS 組態檔案。
  4. 檢查是否有其他可能影響測試撰寫流程的組態問題。
  5. 將發現結果回報給使用者。

撰寫測試

當要求撰寫新測試時,請勿修改應用程式碼或現有測試。

首先,按照評估模組的步驟評估現有測試。使用範本建立新的簡報檔案,並在其中記錄初始覆蓋率和突變分數。

其次,嚴格遵循以下流程撰寫測試:

對模組介面中的每個函數/方法執行以下步驟,完成一個函數後再繼續下一個:

  1. 透過檢查註解、函數名稱、型別簽章以及使用方式,確定函數的用途。如果不確定,請在簡報中記錄此情況,跳過撰寫測試,繼續處理下一個函數。
  2. 記錄函數實作中可能存在的錯誤。
  3. 根據函數用途,找出函數對其參數和隱含輸入所做的錯誤假設,並記錄可能導致函數行為不正確的輸入案例。例如,一個接受正整數但未驗證輸入的函數,就是對其輸入做出了錯誤假設。
  4. 切勿修改既有測試。如果在現有測試中發現任何問題,請通知使用者。
  5. 透過以下步驟撰寫新測試: a. 使用 fast-check 建立基於屬性的測試樁,arbitraries 應涵蓋函數的所有可能參數和隱含輸入。當被測試函數是非同步時,使用 fc.asyncProperty + await fc.assert(...)。如有疑慮,寧可讓 arbitraries 過於寬鬆,並在簡報中註明,以便使用者修正。將測試命名為對預期行為的描述。 b. 使用 vitest 在樁內撰寫斷言,涵蓋任何回傳值和副作用,例如拋出例外、變異輸入、變異/重新指派外部作用域的變數等。確保測試斷言的是預期行為,並能捕捉先前識別出的錯誤假設/錯誤,從而正確地失敗。
  6. 重新檢查覆蓋率:pnpm exec vitest run src/foo.test.ts --coverage
  7. 僅突變測試當前函數:pnpm exec stryker run --mutate "src/foo.ts:<startLine>-<endLine>"
  8. 修改新撰寫的測試,並重複步驟 5a 和 5b,直到函數滿足測試標準(如上所列)。
  9. 在簡報中為此函數新增一個章節。

為所有函數撰寫完測試後,請再次確認已為每個函數新增了簡報章節,並完成摘要部分的撰寫。

審計測試

如果要求審計整個專案,請檢查專案範圍的程式碼覆蓋率和突變分數,並建議需要更詳細審計或新增/更新測試的區域。建議應優先關注程式碼庫中最重要且最容易出錯的區域——核心業務邏輯、事件驅動程式碼以及複雜函數。

如果要求審計專案的某個部分,請找出提示範圍內沒有測試的模組或函數,並在審計後簡報中向使用者報告。

對於提示範圍內有測試的每個模組,請按照評估模組的步驟進行。接著,如果在評估修正後仍有未滿足的測試標準,請按照撰寫測試的步驟填補其餘差距。

評估模組

無論是在撰寫新測試前還是審計時,每當需要根據測試標準評估現有測試檔案時,請使用此流程:

  1. 使用 Vitest 檢查程式碼覆蓋率:pnpm exec vitest run src/foo.test.ts --coverage。
  2. 使用 Stryker Mutator 檢查存活的突變體:pnpm exec stryker run --mutate "src/foo.ts"。
  3. 對於模組中已有測試的每個函數:
    • 針對基於屬性的測試:
      • 尋找函數會接受(不會拋出例外或回傳錯誤值)但 arbitraries 無法產生的輸入。根據需要擴大 arbitraries 以包含此類輸入。
      • 尋找 arbitraries 未涵蓋的輸入,尤其是隱含輸入,例如類別欄位、全域變數、日期/時間和隨機產生的數字。為這些輸入新增 arbitraries。
    • 修正模擬實作中的任何錯誤。
    • 尋找效能瓶頸。
  4. 將覆蓋率、突變分數以及任何發現記錄在簡報中。

測試簡報

按照以下範本格式撰寫簡報。如果函數子章節中沒有需要報告的內容,請直接寫「無」。將簡報輸出到 debriefs/ 目錄下的 Markdown 檔案中。

簡報:{moduleName/fileName} {yyyy-mm-dd}

摘要

覆蓋率(分支) 之前:X% → 之後:Y% 無法覆蓋的行數:{行數範圍}

突變分數(總計) 之前:X% → 之後:Y%

問題

{錯誤假設、發現的錯誤、測試中發現的問題以及失敗測試的項目符號清單。每個項目應以發現問題的檔案相對於專案根目錄的路徑和行號開頭}

{functionName}

函數用途 {一句話描述函數應該要做什麼} (信心程度:高 | 不確定) {如果不確定:不清楚的內容及其原因}

發現的錯誤假設

  • (第 {行號/範圍} 行):{函數未驗證的輸入型別/範圍/形狀假設}

實作中發現的錯誤

  • (第 {行號/範圍} 行):{描述 + 行號}

既有測試中發現的問題

  • (第 {行號/範圍} 行):{問題描述 + 測試名稱}

撰寫的測試案例

  • {每個測試的屬性或不變量的簡短描述}

失敗的測試

  • (第 {行號/範圍} 行):{測試名稱 + 原因}

無法殺死的突變體

  • (第 {行號/範圍} 行):{簡短描述}

相關技能