本文目录导读:

Git钩子脚本运行单元测试是实现持续集成和代码质量保证的有效方式,以下是详细的操作指南:
常用的Git钩子类型
适合运行测试的钩子
| 钩子名称 | 触发时机 | 用途 |
|---|---|---|
pre-commit |
执行git commit前 |
阻止提交到有失败测试的代码 |
pre-push |
执行git push前 |
阻止推送有失败测试的代码 |
commit-msg |
提交消息编辑后 | 验证提交信息格式 |
基础示例:pre-commit 钩子
创建钩子文件
# 进入项目根目录的 .git/hooks 目录 cd .git/hooks # 创建 pre-commit 钩子(去除默认的 .sample 后缀) touch pre-commit chmod +x pre-commit
Python项目的pre-commit钩子
#!/bin/bash
# .git/hooks/pre-commit
echo "🚀 运行单元测试..."
# 保存当前暂存的文件列表
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.py$')
if [ -z "$STAGED_FILES" ]; then
echo "✅ 没有需要测试的Python文件"
exit 0
fi
# 运行单元测试(仅测试被修改的文件)
python -m pytest $STAGED_FILES -v
if [ $? -ne 0 ]; then
echo "❌ 测试失败,提交被阻止"
exit 1
fi
echo "✅ 所有测试通过"
exit 0
JavaScript/TypeScript项目的pre-commit钩子
#!/bin/bash
# .git/hooks/pre-commit
echo "🏃 运行单元测试..."
# 运行所有测试
npm test
if [ $? -ne 0 ]; then
echo "❌ 测试失败,提交被阻止"
exit 1
fi
# 或者在lint-staged模式下只测试修改的文件
# npx jest --bail --findRelatedTests $(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.(js|ts|jsx|tsx)$')
echo "✅ 所有测试通过"
exit 0
进阶:限制测试范围(性能优化)
仅测试修改的文件
#!/bin/bash
# 更高效的pre-commit钩子
function run_tests_for_staged() {
local staged_files=$(git diff --cached --name-only --diff-filter=ACMR)
if [ -z "$staged_files" ]; then
echo "没有暂存的文件"
exit 0
fi
# 根据项目类型选择测试工具
if [ -f "pytest.ini" ] || [ -f "setup.cfg" ]; then
echo "🔍 检测到Python项目"
python -m pytest $(echo "$staged_files" | grep '\.py$') -v --tb=short
elif [ -f "package.json" ]; then
echo "🔍 检测到Node.js项目"
npx jest --bail --findRelatedTests $(echo "$staged_files" | grep -E '\.(js|ts|jsx|tsx)$')
else
echo "未知项目类型,运行所有测试"
npm test || python -m pytest
fi
}
run_tests_for_staged
if [ $? -ne 0 ]; then
echo "❌ 测试失败"
exit 1
fi
使用工具管理钩子
推荐使用工具来管理钩子,避免手动操作:
使用 Husky (JavaScript/TypeScript)
# 安装 husky npm install husky --save-dev # 初始化 husky npx husky install # 创建 pre-commit 钩子 npx husky add .husky/pre-commit "npm test" # .husky/pre-commit 文件内容 #!/bin/sh . "$(dirname "$0")/_/husky.sh" npm test
使用 pre-commit (Python/多语言)
# 安装 pre-commit
pip install pre-commit
# 创建 .pre-commit-config.yaml
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: local
hooks:
- id: pytest
name: pytest
entry: pytest
language: system
types: [python]
pass_filenames: false
always_run: true
args: ['-v', '--tb=short']
EOF
# 安装钩子
pre-commit install
最佳实践
速度优化
#!/bin/bash
# 优化后的 pre-commit 钩子
# 1. 并行执行测试(如果支持)
function run_tests_parallel() {
echo "Running tests in parallel..."
pytest -n auto -v &
TEST_PID=$!
wait $TEST_PID
}
# 2. 缓存测试结果
pytest --cache-clear --last-failed
# 3. 只测试受影响的模块
git diff --cached --name-only | grep '\.py$' | xargs pytest -v
跳过测试的特殊情况
#!/bin/bash
# 允许带参数跳过测试
SKIP_TESTS=$(git log -1 --format=%B | grep -c "\[skip-tests\]")
if [ "$SKIP_TESTS" -gt 0 ]; then
echo "⏭️ 跳过测试(commit message包含 [skip-tests])"
exit 0
fi
# 或者通过环境变量
if [ "$SKIP_GIT_HOOKS" = "true" ]; then
echo "⏭️ 跳过测试(环境变量 SKIP_GIT_HOOKS=true)"
exit 0
fi
# 正常运行测试
npm test
完整的生产环境示例
#!/bin/bash
# .git/hooks/pre-commit - 生产环境版本
set -euo pipefail
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${YELLOW}🏃 启动预提交检查...${NC}"
# 1. 语法检查
echo -e "${YELLOW}📝 检查代码语法...${NC}"
if [ -f "package.json" ]; then
npx eslint --quiet .
elif [ -f "setup.cfg" ]; then
python -m py_compile $(git diff --cached --name-only --diff-filter=ACMR | grep '\.py$')
fi
# 2. 运行单元测试
echo -e "${YELLOW}🧪 运行单元测试...${NC}"
if [ -f "package.json" ]; then
npm test -- --bail --silent
elif [ -f "pytest.ini" ] || [ -f "setup.cfg" ]; then
python -m pytest -v --tb=short --disable-warnings -q
fi
if [ $? -ne 0 ]; then
echo -e "${RED}❌ 测试失败,提交被阻止${NC}"
echo -e "${YELLOW}💡 提示: 如果需要跳过测试,使用 'git commit --no-verify'${NC}"
exit 1
fi
echo -e "${GREEN}✅ 所有检查通过,可以提交${NC}"
exit 0
注意事项
- 性能影响:测试运行会增加提交时间,建议只运行必要的测试
- 依赖管理:确保测试环境在钩子中可用(如虚拟环境、node_modules)
- 跨平台兼容:注意路径分隔符和命令的兼容性
- 错误处理:提供清晰的错误信息和跳过选项
- 版本控制:将钩子脚本纳入项目仓库(非.git/hooks目录,而是使用工具管理)
使用Git钩子运行单元测试可以显著提高代码质量,但需要平衡速度和覆盖范围。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播