你的 100% Coverage 是假的 — 用 test-audit 抓出沒被真正測到的分支
問題在哪
Coverage 這個指標有一個根本性的問題:它只告訴你「這行有沒有被執行到」,不告訴你「這個行為有沒有被驗證」。
舉個例子,你有一個 function 長這樣:
export function parseConfig(raw: string | null) {
if (!raw) return null;
try {
const config = JSON.parse(raw);
return config.enabled ? config : { ...config, enabled: false };
} catch {
return null;
}
}
這裡有四條路徑:raw 是 null 的 early return、parse 成功且 enabled 為 true、parse 成功但 enabled 為 false、parse 失敗的 catch。
現在寫一個 test:
test("parses valid config", () => {
expect(parseConfig('{"enabled":true}')).toEqual({ enabled: true });
});
跑完 coverage,這個 function 大概顯示 60-70% line coverage。數字看起來不差,但其實只測了一條 happy path。null 的情況?沒測。JSON parse 失敗?沒測。enabled 是 false 的邏輯?也沒測。
這個問題在 AI 產 code 的時代特別嚴重。Copilot、Cursor、Claude Code 都很會生 code,也很會生 test——但生出來的 test 往往只驗證「正常情況下能跑」,error path 和 edge case 經常被跳過。你看到 coverage 是綠的,心裡覺得「應該 OK 吧」,結果 production 一遇到 null 就炸。
test-audit 做什麼
test-audit 是我做的一個 CLI 工具,專門解決這個問題。它交叉分析兩組資料:
- AST 靜態分析 — 用 TypeScript Compiler API 掃你的 source code,抽出每個 exported function 的分支結構:if/else、try/catch、switch/case、early return、ternary
- Istanbul coverage JSON — 讀 vitest 或 jest 跑出來的 coverage 數據
兩邊比對之後,告訴你哪些 function 的哪些分支「表面上被執行了但其實沒被真正測到」。
用法很簡單:
# 先跑你的 test 產 coverage
npx vitest run --coverage
# 再跑 audit
npx test-audit
輸出長這樣:
test-audit report
src/utils/parseConfig.ts
parseConfig() ⚠️ 1/4 branches covered
├─ ✅ if/else → line 3
├─ ❌ early return → line 2
├─ ❌ try/catch → line 4
└─ ❌ ternary → line 5
src/hooks/useAuth.ts
useAuth() ❌ 1/4 branches covered
├─ ✅ if/else → line 10
├─ ❌ try/catch → line 15
├─ ❌ early return → line 20
└─ ❌ switch → line 25
src/utils/formatDate.ts
formatDate() ✅ 3/3 branches covered
Summary: 5/11 branches covered (45%)
2 functions need attention
一眼就能看出哪些 function 需要補 test。
技術實作
整個工具的核心就三塊。
AST Analyzer — 用 ts.createSourceFile 解析 TypeScript(也吃 JavaScript),走訪 AST 找出所有 exported function(包含 export function 和 export const xxx = () =>),然後對每個 function 內部掃分支節點。判斷邏輯不複雜:
ts.isIfStatement→ 有 else 就是if,沒 else 且 then block 只有 return 就是early-returnts.isTryStatement→try-catchts.isSwitchStatement→switch-casets.isConditionalExpression→ternary
每個分支記錄 type 和 line number。
Coverage Reader — Istanbul coverage JSON 的結構其實很直觀:每個檔案有一個 branchMap 記錄所有分支的位置,b 記錄每個分支每條路徑的執行次數。把這兩個 map 解析出來就好。
Branch Matcher — 拿 AST 分析出的分支 line number,去 coverage 的 branchMap 裡找對應的 entry,然後看那個 entry 的所有路徑是不是都有被執行過(counts 都 > 0)。只要有一條路徑是 0,就標記為 uncovered。
整個工具零 LLM 依賴,純靜態分析。不需要 API key,不需要連網,速度快,CI 裡跑完全沒問題。
CI 整合
加一行就能在 CI 裡擋住假覆蓋:
- name: Audit branch coverage
run: |
npx vitest run --coverage
npx test-audit --fail-under 80
--fail-under 80 意思是 branch 覆蓋率低於 80% 就 exit 1,PR 直接擋下來。
也支援 JSON 輸出,方便串接其他工具:
npx test-audit --format json
為什麼不用現有工具
你可能會問:Istanbul 本身不是就有 branch coverage 嗎?有,但它的粒度是「這個 branch 的 if/else 兩條路有沒有都走過」,不會告訴你「這個 function 整體的分支覆蓋狀況」,也不會用 function 為單位彙整給你看。
test-audit 做的事情是把 AST 層面的「這個 function 有幾個分支點」跟 coverage 層面的「哪些路徑被走過」交叉比對,然後用 function 為單位產出報告。你一眼就知道哪個 function 需要補 test,而不是盯著一堆紅紅綠綠的行號自己推理。
老實說
這個工具目前的 scope 刻意收得很窄:只看 exported function、只分析五種分支類型、只吃 Istanbul JSON。沒有 React component 層級的分析,沒有自動產 test,沒有 LLM 整合。
我覺得一個 CLI 工具最重要的是「跑一次就知道問題在哪」,而不是「什麼都能做但每次都要設定半天」。之後如果真的需要,再往 VS Code extension 或更細的分析擴展。
source code 在 GitHub,歡迎開 issue 或 PR。
— BridgeCraft,2026
Waiting7777
WoW Arena 冠軍轉前端,用電競 meta 思維拆解技術趨勢。
相關文章
AI Agents開發者工具+1Orca 七個月拿下 10 萬顆星:我拆了它的 GitHub 數據,看到 agent 開發真正的瓶頸
一個由 agent 寫出來、拿來管 agent 的開源 IDE。19,000 個 commit、979 次 release 背後,最值得看的數字是 2,525 個放了一個月還沒人處理的 PR。
2026年10月20日 · Waiting7777 · 7 分鐘閱讀
繼續閱讀 →
AI Agents自動化測試+1跑了兩週 Agent QA Pipeline — 真實數字、踩到的坑、重來我會怎麼做
這是 post-mortem,不是 case study。做對的三件事、做錯的三件事、以及為什麼這套東西接下來要抽成 `claude-qa-pipeline` OSS。
2026年10月20日 · Waiting7777 · 6 分鐘閱讀
繼續閱讀 →
AI Agents自動化測試+1我用 15 個 Claude Code Agent 跑了一條自動 QA Pipeline — 兩週 17 個 merged PR
這不是 proof of concept,是跑在 macOS launchd 上的真實系統:每天兩班、spec-driven、自己開 Linear issue、自己發 PR、自己從過去的 fix 裡學新的 pattern。
2026年10月20日 · Waiting7777 · 5 分鐘閱讀
繼續閱讀 →這篇文章對你有幫助嗎?
每週一篇 — 技術趨勢背後的商業邏輯
AI 產業在變什麼、工程師該注意什麼——拆清楚寄到你的信箱。