Claude Code Hooks 自动运行测试怎么做:PostToolUse、Stop 和最小验证流程

Claude Code Hooks 自动运行测试的实操教程:什么时候用 PostToolUse,什么时候用 Stop,如何按文件类型选择最小测试命令,并避免每次改动都触发全量测试。

Claude Code hooks 自动运行测试,最适合解决一个常见问题:你已经在 CLAUDE.md 里写了“改完代码请运行测试”,但 Claude Code 有时会忘,有时会跑太大的测试,有时测试失败后只给你一段泛泛解释。

Hooks 的价值在于把“希望它记得”变成“事件发生时一定执行”。官方文档把 hooks 描述为在 Claude Code 生命周期特定节点自动执行的用户自定义命令,可以用来格式化代码、拦截危险操作、发送通知、注入上下文和执行验证。对测试来说,最常用的是 PostToolUseStop

快速结论

如果只是想在 Claude Code 编辑文件后自动跑相关检查,优先用 PostToolUse,并把 matcher 限定在 Edit|Write

如果你想在 Claude Code 准备结束一轮任务时做最终验证,适合用 Stop。但不要一上来就在 Stop 里跑全量测试,否则一次小改动也可能变成很慢、很贵、很吵的流程。

比较稳的做法是:

  1. PostToolUse 只做轻量检查,例如格式化、类型检查片段、按文件名选择测试。
  2. Stop 做最终提醒或运行一个更窄的验证脚本。
  3. 测试失败时只输出关键摘要,不要把完整日志塞回上下文。
  4. 项目级 hooks 放在 .claude/settings.json,具体逻辑放到 .claude/hooks/ 脚本里。
  5. 先在一个小项目里试运行,再推广到团队仓库。

先想清楚:自动运行什么测试

很多人配置 hooks 的第一反应是:

1
npm test

这通常不是最佳起点。自动化测试越靠近 Claude Code 的编辑动作,就越应该短、准、可重复。

更好的分层是:

场景 建议命令
改了单个前端组件 相关单测、类型检查、lint
改了后端函数 相关 package 的单测
改了配置文件 配置校验、构建 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

文章下面先用 Bash 示例说明思路,再给 PowerShell 版本。真正落地时,按项目团队常用 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

这个脚本故意比较保守。它没有直接跑全量测试,也没有把日志写得很长。你可以按项目实际情况改成 pnpm testbun testpytest path/to/test.pygo test ./pkg/foo 等命令。

PowerShell 版本

如果团队主要在 Windows 上用 Claude Code,可以写 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\""
          }
        ]
      }
    ]
  }
}

注意:PowerShell 字符串、路径和引号很容易出问题。先在普通终端里手动运行脚本,再放进 hook。

测试失败后 Claude Code 会怎样

Hook 命令的输出会进入 Claude Code 的工作流。测试失败时,Claude Code 通常会看到错误输出,然后继续修复。

但这里有两个坑。

第一,失败输出不要太长。完整测试日志、快照 diff、依赖安装日志可能非常大,会把上下文撑爆。脚本里可以只保留最后几十行:

1
npm test -- "$FILE_PATH" --runInBand 2>&1 | tail -n 80

第二,测试命令要稳定。不要让 hook 依赖交互式输入、随机端口、临时网络服务或需要人工确认的命令。Hooks 适合确定性检查,不适合复杂人工流程。

用 Stop 做最终检查

PostToolUse 更适合“编辑后立即检查”。Stop 更适合“这一轮准备结束前再确认一下”。

例如你可以在 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 的团队:先让 Claude Code 在结束前看见检查清单,确认不会打扰正常工作,再逐步把某些检查自动化。

什么时候不要自动跑测试

并不是所有项目都适合一编辑就跑测试。

下面几种情况要谨慎:

  • 单测很慢,每次运行超过 30 秒;
  • 测试需要数据库、队列、浏览器或外部 API;
  • 测试会改本地数据;
  • 测试日志很大;
  • 项目还没有稳定的最小测试命令;
  • 团队经常让 Claude Code 批量改很多文件。

这种情况下,可以先把 hook 改成“提示应运行什么命令”,而不是自动执行。

例如:

1
echo "Changed $FILE_PATH. Recommended check: npm run typecheck && npm test -- related-file"

这样不会拖慢 Claude Code,也能把测试习惯固定下来。

一个更实用的策略:按风险分级

可以把自动测试分成三档。

风险 自动动作
低风险 格式化、lint 单文件、Markdown 检查
中风险 类型检查、相关单测、配置校验
高风险 全量测试、E2E、构建、数据库迁移检查

低风险可以放进 PostToolUse 自动跑。中风险可以按文件类型触发。高风险最好放到人工确认、CI 或发布流程里。

Claude Code hooks 的目标不是让本地环境变成完整 CI,而是让 Agent 少犯低级错。

与 CLAUDE.md 怎么分工

CLAUDE.md 适合写原则:

1
2
3
改动完成后优先运行最小相关测试。
测试失败时先修复失败原因,不要绕过测试。
不要为了一次小改动运行全量 E2E,除非用户要求。

Hooks 适合写执行:

1
2
当 Edit 或 Write 发生后,运行 run-related-tests.sh。
当 Stop 发生时,输出最终检查建议。

也就是说,CLAUDE.md 告诉 Claude Code “应该怎么想”,hooks 负责“到点就做”。两者配合,比只靠提示词稳定。

排错清单

如果 hook 没有运行,按这个顺序排查。

  1. 在 Claude Code 里输入 /hooks,看事件是否注册。
  2. 确认 settings.json 是合法 JSON。
  3. 确认事件名写对,例如 PostToolUseStop
  4. 确认 matcher 能匹配工具名,例如 Edit|Write
  5. 确认脚本路径正确。
  6. macOS/Linux 确认脚本有执行权限。
  7. Windows 确认 PowerShell 执行策略和引号没问题。
  8. 脚本里先打印一行 echo hook fired 做最小验证。
  9. 不要一开始就接复杂测试命令。
  10. 如果输出太长,先改成摘要。

团队使用建议

团队仓库里启用 Claude Code hooks,建议先从最小版本开始。

第一步,只加通知或最终检查提醒。

第二步,对前端格式化、Markdown、类型检查这类低风险命令自动化。

第三步,再按目录或文件类型增加相关单测。

第四步,把全量测试交给 CI,不要强行塞进每次 Claude Code 编辑后。

如果团队成员使用 macOS、Linux、Windows 混合环境,尽量把核心逻辑写成跨平台脚本,或者分别提供 shps1,并在 README 里写清楚启用方式。

参考资料

总结

Claude Code hooks 自动运行测试的关键,不是把 npm test 塞进配置,而是把验证流程拆细。

编辑后用 PostToolUse 做轻量、相关、可重复的检查;结束前用 Stop 做最终提醒或窄范围验证;全量测试仍然交给人工确认和 CI。这样 Claude Code 能及时看到错误,又不会因为每次小改动都跑大测试而变慢、变吵、变贵。

把规则写进 CLAUDE.md,把动作放进 .claude/hooks/,再用 /hooks 验证注册状态。这个组合,比单纯提醒“记得跑测试”可靠得多。

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计