Claude Code hooks 自動執行測試,最適合解決一個常見問題:你已經在 CLAUDE.md 裡寫了「改完程式碼請執行測試」,但 Claude Code 有時會忘記,有時會跑太大的測試,有時測試失敗後只給一段泛泛解釋。
Hooks 的價值,是把「希望它記得」變成「事件發生時一定執行」。對測試來說,最常用的是 PostToolUse 和 Stop。
快速結論
如果只是想在 Claude Code 編輯文件後自動跑相關檢查,優先用 PostToolUse,並把 matcher 限定在 Edit|Write。
如果想在 Claude Code 準備結束一輪任務時做最後驗證,適合用 Stop。但不要一開始就在 Stop 裡跑全量測試,否則一次小改動也可能變慢、變貴、變吵。
比較穩的做法是:
PostToolUse 只做輕量檢查,例如格式化、型別檢查、按文件名選擇測試。
Stop 做最終提醒或執行較窄的驗證腳本。
- 測試失敗時只輸出關鍵摘要,不把完整日誌塞回上下文。
- 專案級 hooks 放在
.claude/settings.json,具體邏輯放到 .claude/hooks/ 腳本。
- 先在小專案試跑,再推廣到團隊倉庫。
先想清楚:自動執行什麼測試
很多人配置 hooks 的第一反應是:
這通常不是最佳起點。自動化測試越靠近 Claude Code 的編輯動作,就越應該短、準、可重複。
| 場景 |
建議命令 |
| 改了單個前端元件 |
相關單測、型別檢查、lint |
| 改了後端函式 |
相關 package 的單測 |
| 改了配置文件 |
配置校驗、build dry run |
| 改了 Markdown |
連結檢查或 front matter 檢查 |
| 準備結束任務 |
彙總執行一次最小驗證 |
| 準備提交或發布 |
手動執行完整測試和構建 |
Hooks 不應取代 CI。它更像本地安全網:及時發現明顯錯誤,減少 Claude Code 在錯誤方向上連續改文件。
推薦目錄結構
不要把複雜邏輯全部塞進 settings.json 的一行命令裡。
1
2
3
4
5
6
|
your-repo/
.claude/
settings.json
hooks/
run-related-tests.sh
final-check.sh
|
Windows 專案也可以用 PowerShell:
1
2
3
4
5
6
|
your-repo/
.claude/
settings.json
hooks/
run-related-tests.ps1
final-check.ps1
|
真正落地時,按團隊常用 shell 選一種即可。
用 PostToolUse 在編輯後跑相關測試
PostToolUse 會在工具呼叫之後觸發。自動測試場景通常只關心寫文件工具,所以 matcher 可以限制為 Edit|Write。
.claude/settings.json 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-related-tests.sh"
}
]
}
]
}
}
|
這個配置只負責註冊 hook。真正判斷「改了什麼文件、該跑什麼測試」,放到腳本裡。
Bash 腳本:按文件類型選擇命令
.claude/hooks/run-related-tests.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#!/usr/bin/env bash
set -euo pipefail
INPUT="$(cat)"
FILE_PATH="$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [ -z "$FILE_PATH" ]; then
exit 0
fi
case "$FILE_PATH" in
*.ts|*.tsx)
echo "Running TypeScript checks for $FILE_PATH"
npm run typecheck -- --pretty false
;;
*.test.ts|*.spec.ts|*.test.tsx|*.spec.tsx)
echo "Running focused test for $FILE_PATH"
npm test -- "$FILE_PATH" --runInBand
;;
*.py)
echo "Running Python checks for $FILE_PATH"
python -m pytest -q
;;
*.md)
echo "Markdown changed: skip code tests"
;;
*)
echo "No related test rule for $FILE_PATH"
;;
esac
|
然後賦予執行權限:
1
|
chmod +x .claude/hooks/run-related-tests.sh
|
這個腳本刻意保守。它不直接跑全量測試,也不輸出很長日誌。
PowerShell 版本
.claude/hooks/run-related-tests.ps1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
$inputJson = [Console]::In.ReadToEnd()
if ([string]::IsNullOrWhiteSpace($inputJson)) {
exit 0
}
$payload = $inputJson | ConvertFrom-Json
$filePath = $payload.tool_input.file_path
if ([string]::IsNullOrWhiteSpace($filePath)) {
exit 0
}
switch -Regex ($filePath) {
'\.tsx?$' {
Write-Output "Running TypeScript checks for $filePath"
npm run typecheck -- --pretty false
break
}
'\.(test|spec)\.tsx?$' {
Write-Output "Running focused test for $filePath"
npm test -- $filePath --runInBand
break
}
'\.py$' {
Write-Output "Running Python checks for $filePath"
python -m pytest -q
break
}
'\.md$' {
Write-Output "Markdown changed: skip code tests"
break
}
default {
Write-Output "No related test rule for $filePath"
}
}
|
對應的 .claude/settings.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "powershell.exe -ExecutionPolicy Bypass -File \"$CLAUDE_PROJECT_DIR/.claude/hooks/run-related-tests.ps1\""
}
]
}
]
}
}
|
先在普通終端手動跑腳本,再放進 hook。
測試失敗時 Claude Code 會怎樣
Hook 命令輸出會進入 Claude Code 的工作流。測試失敗時,Claude Code 通常會看到錯誤輸出,然後繼續修復。
這裡有兩個坑。
第一,失敗輸出不要太長。可以只保留最後幾十行:
1
|
npm test -- "$FILE_PATH" --runInBand 2>&1 | tail -n 80
|
第二,測試命令要穩定。不要讓 hook 依賴互動式輸入、隨機端口、臨時網路服務或人工確認。
用 Stop 做最終檢查
PostToolUse 適合「編輯後立即檢查」。Stop 適合「這一輪準備結束前再確認」。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/final-check.sh"
}
]
}
]
}
}
|
.claude/hooks/final-check.sh:
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/usr/bin/env bash
set -euo pipefail
if git diff --quiet; then
echo "No working tree changes. Skip final checks."
exit 0
fi
echo "Working tree changed. Suggested final checks:"
echo "- run the focused test command for touched files"
echo "- run typecheck if code changed"
echo "- run full test suite before commit or deploy"
|
剛開始啟用 hooks 的團隊,可以先用提醒版,不要立刻自動跑昂貴命令。
什麼時候不要自動跑測試
以下情況要謹慎:
- 單測很慢;
- 測試需要資料庫、佇列、瀏覽器或外部 API;
- 測試會改本地資料;
- 測試日誌很大;
- 專案還沒有穩定的最小測試命令;
- 團隊經常讓 Claude Code 批量改很多文件。
這種情況下,hook 可以先提示應執行什麼命令,而不是自動執行。
按風險分級
| 風險 |
自動動作 |
| 低風險 |
格式化、lint 單文件、Markdown 檢查 |
| 中風險 |
型別檢查、相關單測、配置校驗 |
| 高風險 |
全量測試、E2E、構建、資料庫遷移檢查 |
低風險可以放進 PostToolUse。中風險可以按文件類型觸發。高風險最好放到人工確認、CI 或發布流程裡。
和 CLAUDE.md 怎麼分工
CLAUDE.md 適合寫原則:
1
2
3
|
改動完成後優先執行最小相關測試。
測試失敗時先修復失敗原因,不要繞過測試。
不要為了一次小改動執行全量 E2E,除非使用者要求。
|
Hooks 適合寫執行:
1
2
|
當 Edit 或 Write 發生後,執行 run-related-tests.sh。
當 Stop 發生時,輸出最終檢查建議。
|
CLAUDE.md 告訴 Claude Code 應該怎麼想;hooks 負責到點就做。
排錯清單
如果 hook 沒有執行:
- 在 Claude Code 輸入
/hooks,確認事件是否註冊。
- 確認
settings.json 是合法 JSON。
- 確認事件名正確,例如
PostToolUse、Stop。
- 確認 matcher 能匹配工具名,例如
Edit|Write。
- 確認腳本路徑正確。
- macOS/Linux 確認腳本有執行權限。
- Windows 確認 PowerShell 執行策略和引號沒問題。
- 先印一行
echo hook fired 做最小驗證。
- 不要一開始就接複雜測試命令。
- 如果輸出太長,先改成摘要。
團隊使用建議
團隊倉庫啟用 Claude Code hooks,建議從最小版本開始:
- 只加通知或最終檢查提醒。
- 自動化前端格式化、Markdown、型別檢查這類低風險命令。
- 再按目錄或文件類型增加相關單測。
- 全量測試交給 CI,不要塞進每次 Claude Code 編輯後。
參考資料
總結
Claude Code hooks 自動執行測試的關鍵,不是把 npm test 塞進配置,而是把驗證流程拆細。
編輯後用 PostToolUse 做輕量、相關、可重複的檢查;結束前用 Stop 做最終提醒或窄範圍驗證;全量測試仍然交給人工確認和 CI。把規則寫進 CLAUDE.md,把動作放進 .claude/hooks/,再用 /hooks 驗證註冊狀態。