本文目录导读:

我来介绍几种常见的代码覆盖率统计脚本编写方法,根据不同语言和工具进行分类:
Python 覆盖率统计
使用 pytest-cov
#!/bin/bash
# coverage_python.sh
# 安装依赖
pip install pytest pytest-cov coverage
# 运行测试并生成覆盖率报告
pytest --cov=./src \
--cov-report=term-missing \
--cov-report=html:coverage_html \
--cov-report=xml:coverage.xml \
tests/
更完整的 Python 脚本
#!/usr/bin/env python3
# coverage_stats.py
import subprocess
import sys
import os
def run_coverage():
"""运行覆盖率统计"""
# 配置参数
source_dir = 'src' # 源代码目录
test_dir = 'tests' # 测试目录
output_dir = 'coverage_reports'
# 创建输出目录
os.makedirs(output_dir, exist_ok=True)
# 运行覆盖率检查
commands = [
f'coverage run --source={source_dir} -m pytest {test_dir}',
f'coverage report -m',
f'coverage html -d {output_dir}/html',
f'coverage xml -o {output_dir}/coverage.xml'
]
for cmd in commands:
print(f"执行: {cmd}")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"错误: {result.stderr}")
sys.exit(1)
print(result.stdout)
# 解析覆盖率数据
print(f"\n覆盖率报告已生成到: {output_dir}/html/")
if __name__ == '__main__':
run_coverage()
JavaScript/TypeScript 覆盖率
使用 Istanbul/nyc
// package.json 配置
{
"scripts": {
"coverage": "nyc --reporter=html --reporter=text --reporter=lcov npm test",
"coverage:check": "nyc --check-coverage --lines=80 --functions=80 --branches=80 npm test"
}
}
Shell 脚本版本
#!/bin/bash
# coverage_js.sh
# 配置
COVERAGE_THRESHOLD=80
PROJECT_DIR=$(pwd)
echo "=== JavaScript 覆盖率统计 ==="
# 安装依赖
npm install --save-dev nyc mocha
# 运行测试并生成覆盖率
npx nyc \
--reporter=html \
--reporter=text \
--reporter=lcov \
--report-dir=./coverage \
npm test
# 检查覆盖率是否达标
if [ $? -eq 0 ]; then
echo "覆盖率报告生成成功"
# 解析覆盖率数据
COVERAGE=$(npx nyc report --reporter=text-summary | grep Lines | awk '{print $3}' | sed 's/%//')
echo "当前行覆盖率: ${COVERAGE}%"
if (( $(echo "$COVERAGE < $COVERAGE_THRESHOLD" | bc -l) )); then
echo "警告: 覆盖率低于 ${COVERAGE_THRESHOLD}%"
exit 1
fi
else
echo "覆盖率测试失败"
exit 1
fi
Java 覆盖率统计
使用 JaCoCo
<!-- pom.xml 配置 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
Shell 脚本版本
#!/bin/bash
# coverage_java.sh
# 配置
MIN_COVERAGE=80
JACOCO_VERSION=0.8.11
echo "=== Java 覆盖率统计 ==="
# 检查工具
command -v mvn >/dev/null 2>&1 || { echo "需要 Maven"; exit 1; }
# 运行测试并生成覆盖率
mvn clean test jacoco:report
# 检查覆盖率
if [ -f "target/site/jacoco/jacoco.csv" ]; then
# 解析 CSV 文件获取覆盖率
LINE_COVERAGE=$(awk -F',' 'NR>1 {sum+=$8; count++} END {printf "%.2f", sum/count}' target/site/jacoco/jacoco.csv)
echo "代码行覆盖率: ${LINE_COVERAGE}%"
if (( $(echo "$LINE_COVERAGE < $MIN_COVERAGE" | bc -l) )); then
echo "覆盖率低于 ${MIN_COVERAGE}%"
exit 1
fi
fi
通用覆盖率统计脚本
#!/usr/bin/env python3
# universal_coverage.py
import os
import sys
import json
import subprocess
from pathlib import Path
class CoverageCollector:
"""通用的覆盖率收集器"""
def __init__(self, config_file='.coveragerc'):
self.config = self._load_config(config_file)
self.results = {}
def _load_config(self, config_file):
"""加载配置文件"""
default_config = {
'python': {
'enabled': True,
'source': 'src',
'test_dir': 'tests',
'threshold': 80
},
'javascript': {
'enabled': True,
'test_dir': 'test',
'threshold': 80
},
'java': {
'enabled': True,
'threshold': 80
}
}
if os.path.exists(config_file):
with open(config_file, 'r') as f:
return json.load(f)
return default_config
def collect_python_coverage(self):
"""收集 Python 覆盖率"""
print("\n=== Python 覆盖率 ===")
try:
cmd = [
'coverage', 'run',
f'--source={self.config["python"]["source"]}',
'-m', 'pytest', self.config['python']['test_dir']
]
subprocess.run(cmd, check=True)
result = subprocess.run(
['coverage', 'report', '--format=json'],
capture_output=True, text=True
)
if result.returncode == 0:
data = json.loads(result.stdout)
return {
'lines': data.get('totals', {}).get('percent_covered', 0),
'branches': data.get('totals', {}).get('percent_covered_branches', 0)
}
except Exception as e:
print(f"Python 覆盖率失败: {e}")
return None
def collect_js_coverage(self):
"""收集 JavaScript 覆盖率"""
print("\n=== JavaScript 覆盖率 ===")
try:
subprocess.run(
['npx', 'nyc', '--reporter=json', 'npm', 'test'],
check=True
)
if os.path.exists('.nyc_output/coverage.json'):
with open('.nyc_output/coverage.json', 'r') as f:
data = json.load(f)
total_lines = total_covered = 0
for file_data in data.values():
for line, count in file_data.get('s', {}).items():
total_lines += 1
if count > 0:
total_covered += 1
return {
'lines': (total_covered / total_lines * 100) if total_lines > 0 else 0
}
except Exception as e:
print(f"JavaScript 覆盖率失败: {e}")
return None
def generate_report(self):
"""生成覆盖率报告"""
report = {
'timestamp': None,
'project': os.getcwd(),
'results': {}
}
from datetime import datetime
report['timestamp'] = datetime.now().isoformat()
# 收集各语言覆盖率
if self.config.get('python', {}).get('enabled', False):
report['results']['python'] = self.collect_python_coverage()
if self.config.get('javascript', {}).get('enabled', False):
report['results']['javascript'] = self.collect_js_coverage()
# 保存报告
report_file = 'coverage_report.json'
with open(report_file, 'w') as f:
json.dump(report, f, indent=2)
print(f"\n报告已保存到: {report_file}")
# 检查覆盖率阈值
self._check_thresholds(report)
return report
def _check_thresholds(self, report):
"""检查覆盖率是否达标"""
all_passed = True
for lang, data in report['results'].items():
if data:
threshold = self.config.get(lang, {}).get('threshold', 80)
coverage = data.get('lines', 0)
if coverage < threshold:
print(f"⚠ {lang}: {coverage:.1f}% (低于 {threshold}%)")
all_passed = False
else:
print(f"✓ {lang}: {coverage:.1f}%")
if not all_passed:
print("\n❌ 部分语言未达到覆盖率要求")
sys.exit(1)
else:
print("\n✅ 所有语言覆盖率达标")
if __name__ == '__main__':
collector = CoverageCollector()
collector.generate_report()
配置示例 (.coveragerc)
{
"python": {
"enabled": true,
"source": "src",
"test_dir": "tests",
"threshold": 85,
"exclude": ["tests/*", "migrations/*", "setup.py"]
},
"javascript": {
"enabled": true,
"test_dir": "test",
"threshold": 80,
"exclude": ["node_modules/*", "dist/*"]
},
"java": {
"enabled": true,
"threshold": 85,
"exclude": ["test/*", "generated/*"]
}
}
CI/CD 集成脚本
# .github/workflows/coverage.yml
name: Code Coverage
on: [push, pull_request]
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install pytest pytest-cov coverage
npm install
- name: Run coverage
run: python coverage_universal.py
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
files: ./coverage.xml,./coverage/lcov.info
flags: unittests
name: codecov-umbrella
这些脚本可以根据具体项目需求进行调整和组合使用,选择哪种方案主要取决于:
- 项目使用的编程语言
- 现有的测试框架
- CI/CD 集成需求
- 是否需要多语言支持
建议从简单的单语言脚本开始,根据需要逐步扩展。