📦 Test Impact Analyzer — Test Impact 分析器
v1.0.0Determine which tests need to 运行 for a given code change — 追踪 file dependencies, map source-to-test relationships, identify untested changes, and priorit...
详细分析 ▾
运行时依赖
版本
Given a 设置 of changed files (from git diff), find all tests that should 运行.
安装命令
点击复制技能文档
测试影响分析器 无需每次改动都跑全量测试。分析变更的源文件,追踪依赖,定位对应测试,生成精准测试执行计划。CI 更快,测试更聚焦,反馈更即时。 适用场景:“我该跑哪些测试”“这次改动影响什么”“测试影响分析”“优化 CI 测试时长”“哪些测试覆盖此文件”“跳过无关测试”或加速 CI 流水线。
命令
- affected — 找出受影响的测试
# 获取变更文件(对比 main/master) BASE_BRANCH="${1:-main}" CHANGED_FILES=$(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null || git diff --name-only HEAD~1 2>/dev/null) if [ -z "$CHANGED_FILES" ]; then echo "未检测到变更文件。请指定基准分支或确保处于功能分支。" exit 0 fi echo "变更文件:" echo "$CHANGED_FILES" | sed 's/^/ /' echo ""
步骤 1:直接测试匹配 echo "=== 直接测试匹配 ===" echo "$CHANGED_FILES" | while read f; do # 跳过非源文件 echo "$f" | grep -qE '\.(ts|js|tsx|jsx|py|go|rs|java)$' || continue # 跳过测试文件本身 echo "$f" | grep -qE '\.(test|spec)\.' && continue BASE=$(basename "$f" | sed 's/\.[^.]$//') DIR=$(dirname "$f") # 查找对应测试文件 for pattern in "${BASE}.test.ts" "${BASE}.test.js" "${BASE}.test.tsx" "${BASE}.test.jsx" \ "${BASE}.spec.ts" "${BASE}.spec.js" "${BASE}.spec.tsx" \ "test_${BASE}.py" "${BASE}_test.py" "${BASE}_test.go"; do FOUND=$(find "$DIR" -maxdepth 2 -name "$pattern" -not -path '/node_modules/' 2>/dev/null | head -1) if [ -n "$FOUND" ]; then echo " $f → $FOUND" break fi done done
步骤 2:引用链分析 echo "" echo "=== 引用链(引用变更文件的文件) ===" echo "$CHANGED_FILES" | while read f; do echo "$f" | grep -qE '\.(ts|js|tsx|jsx)$' || continue echo "$f" | grep -qE '\.(test|spec)\.' && continue BASE=$(basename "$f" | sed 's/\.[^.]$//') DIR=$(dirname "$f") # 查找引用此模块的文件 IMPORTERS=$(rg -l "from ['\"]\../${BASE}['\"]|from ['\"]\./${BASE}['\"]|require\(['\"]\../${BASE}['\"]\)" \ -g '.{ts,js,tsx,jsx}' -g '!node_modules' -g '!dist' 2>/dev/null) if [ -n "$IMPORTERS" ]; then echo " $f 被引用:" echo "$IMPORTERS" | while read imp; do IMP_BASE=$(basename "$imp" | sed 's/\.[^.]$//') # 引用者是否为测试文件? if echo "$imp" | grep -qE '\.(test|spec)\.'; then echo " 🧪 $imp(测试——需执行)" else # 检查引用者是否有自己的测试 TEST=$(find "$(dirname "$imp")" -maxdepth 2 \ -name "${IMP_BASE}.test." -o -name "${IMP_BASE}.spec." 2>/dev/null | head -1) if [ -n "$TEST" ]; then echo " 📄 $imp → 🧪 $TEST(传递)" else echo " 📄 $imp(未找到测试)" fi fi done fi done
步骤 3:汇总待执行测试 echo "" echo "=== 测试执行计划 ===" # 收集唯一测试文件 TESTS_TO_RUN=$(mktemp) echo "$CHANGED_FILES" | while read f; do echo "$f" | grep -qE '\.(test|spec)\.' && echo "$f" >> "$TESTS_TO_RUN" BASE=$(basename "$f" | sed 's/\.[^.]$//') DIR=$(dirname "$f") # 直接测试匹配 find "$DIR" -maxdepth 2 \( -name "${BASE}.test." -o -name "${BASE}.spec." -o -name "test_${BASE}." -o -name "${BASE}_test." \) \ -not -path '/node_modules/' 2>/dev/null >> "$TESTS_TO_RUN" # 来自引用者的测试 IMPORTERS=$(rg -l "from ['\"]\../${BASE}['\"]|from ['\"]\./${BASE}['\"]" \ -g '.{test.,spec.}' -g '!node_modules' 2>/dev/null) echo "$IMPORTERS" >> "$TESTS_TO_RUN" 2>/dev/null done UNIQUE_TESTS=$(sort -u "$TESTS_TO_RUN" | grep -v '^$') TOTAL=$(echo "$UNIQUE_TESTS" | grep -c "." 2>/dev/null || echo "0") ALL_TESTS=$(find . -type f \( -name ".test." -o -name ".spec." -o -name "test_" \) \ -not -path '/node_modules/' 2>/dev/null | wc -l) echo "待执行测试:$TOTAL / $ALL_TESTS 总计($(echo "scale=0; $TOTAL 100 / ($ALL_TESTS + 1)" | bc)%)" echo "" echo "$UNIQUE_TESTS" | sed 's/^/ /' rm -f "$TESTS_TO_RUN"
- map — 源文件到测试映射
echo "=== 源文件 → 测试 映射 ===" find . -type f \( -name ".ts" -o -name ".js" -o -name ".tsx" -o -name ".jsx" -o -name ".py" -o -name ".go" \) \ -not -path '/node_modules/' -not -path '/vendor/' -not -path '/dist/' \ -not -name '.test.' -not -name '.spec.' -not -name 'test_' 2>/dev/null | sort | while read f; do BASE=$(basename "$f" | sed 's/\.[^.]$//') DIR=$(dirname "$f") TEST=$(find "$DIR" -maxdepth 2 \( -name "${BASE}.test." -o -name "${BASE}.spec." -o -name "test_${BASE}." -o -name "${BASE}_test." \) \ -not -path '/node_modules/*' 2>/dev/null