""" 测试报告生成脚本 生成完整的测试报告,包括: - 测试执行摘要 - 覆盖率报告 - 性能测试结果 - 安全测试结果 - Bug清单 """ import os import json import subprocess from datetime import datetime from pathlib import Path class TestReportGenerator: """测试报告生成器""" def __init__(self, project_root: str): self.project_root = Path(project_root) self.report_dir = self.project_root / "test_reports" self.report_dir.mkdir(exist_ok=True) self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") self.report_data = { "timestamp": datetime.now().isoformat(), "project": "资产管理系统", "version": "1.0.0", "summary": {}, "unit_tests": {}, "integration_tests": {}, "e2e_tests": {}, "coverage": {}, "performance": {}, "security": {}, "bugs": [] } def run_unit_tests(self): """运行单元测试""" print("=" * 60) print("运行单元测试...") print("=" * 60) cmd = [ "pytest", "-v", "-m", "unit", "--html=test_reports/unit_test_report.html", "--self-contained-html", "--json-report", "--json-report-file=test_reports/unit_test_results.json" ] result = subprocess.run(cmd, capture_output=True, text=True) # 解析结果 if os.path.exists("test_reports/unit_test_results.json"): with open("test_reports/unit_test_results.json", "r") as f: data = json.load(f) self.report_data["unit_tests"] = { "total": data.get("summary", {}).get("total", 0), "passed": data.get("summary", {}).get("passed", 0), "failed": data.get("summary", {}).get("failed", 0), "skipped": data.get("summary", {}).get("skipped", 0), "duration": data.get("summary", {}).get("duration", 0) } return result.returncode == 0 def run_integration_tests(self): """运行集成测试""" print("\n" + "=" * 60) print("运行集成测试...") print("=" * 60) cmd = [ "pytest", "-v", "-m", "integration", "--html=test_reports/integration_test_report.html", "--self-contained-html" ] result = subprocess.run(cmd, capture_output=True, text=True) return result.returncode == 0 def run_coverage_tests(self): """运行覆盖率测试""" print("\n" + "=" * 60) print("生成覆盖率报告...") print("=" * 60) cmd = [ "pytest", "--cov=app", "--cov-report=html:test_reports/htmlcov", "--cov-report=term-missing", "--cov-report=json:test_reports/coverage.json", "--cov-fail-under=70" ] result = subprocess.run(cmd, capture_output=True, text=True) # 解析覆盖率数据 if os.path.exists("test_reports/coverage.json"): with open("test_reports/coverage.json", "r") as f: data = json.load(f) totals = data.get("totals", {}) self.report_data["coverage"] = { "line_coverage": totals.get("percent_covered", 0), "lines_covered": totals.get("covered_lines", 0), "lines_missing": totals.get("missing_lines", 0), "num_statements": totals.get("num_statements", 0) } return result.returncode == 0 def run_security_tests(self): """运行安全测试""" print("\n" + "=" * 60) print("运行安全测试...") print("=" * 60) cmd = [ "pytest", "-v", "tests/security/", "-m", "security", "--html=test_reports/security_test_report.html" ] result = subprocess.run(cmd, capture_output=True, text=True) return result.returncode == 0 def collect_bugs(self): """收集测试中发现的Bug""" print("\n" + "=" * 60) print("分析测试结果,收集Bug...") print("=" * 60) bugs = [] # 从失败的测试中提取Bug test_results = [ "test_reports/unit_test_results.json", "test_reports/integration_test_results.json" ] for result_file in test_results: if os.path.exists(result_file): with open(result_file, "r") as f: data = json.load(f) for test in data.get("tests", []): if test.get("outcome") == "failed": bugs.append({ "test_name": test.get("name"), "error": test.get("call", {}).get("crash", {}).get("message", ""), "severity": "high" if "critical" in test.get("name", "").lower() else "medium", "status": "open" }) self.report_data["bugs"] = bugs return bugs def generate_html_report(self): """生成HTML测试报告""" print("\n" + "=" * 60) print("生成HTML测试报告...") print("=" * 60) html_template = """ 资产管理系统 - 测试报告

📊 资产管理系统 - 测试报告

{total_tests}
总测试数
{passed_tests}
通过
{failed_tests}
失败
{coverage}%
代码覆盖率

📋 测试摘要

测试类型 总数 通过 失败 通过率
单元测试 {unit_total} {unit_passed} {unit_failed} {unit_pass_rate}%
集成测试 {integration_total} {integration_passed} {integration_failed} {integration_pass_rate}%
E2E测试 {e2e_total} {e2e_passed} {e2e_failed} {e2e_pass_rate}%

🐛 Bug清单 ({bug_count})

""" # 计算统计数据 total_tests = ( self.report_data["unit_tests"].get("total", 0) + self.report_data["integration_tests"].get("total", 0) + self.report_data["e2e_tests"].get("total", 0) ) passed_tests = ( self.report_data["unit_tests"].get("passed", 0) + self.report_data["integration_tests"].get("passed", 0) + self.report_data["e2e_tests"].get("passed", 0) ) failed_tests = ( self.report_data["unit_tests"].get("failed", 0) + self.report_data["integration_tests"].get("failed", 0) + self.report_data["e2e_tests"].get("failed", 0) ) coverage = self.report_data["coverage"].get("line_coverage", 0) # 生成Bug列表HTML bug_items = "" for bug in self.report_data.get("bugs", []): bug_items += f"""
  • {bug.get('test_name', '')}
    {bug.get('error', '')}
  • """ html = html_template.format( total_tests=total_tests, passed_tests=passed_tests, failed_tests=failed_tests, coverage=int(coverage), failed_class="success" if failed_tests == 0 else "danger", coverage_class="success" if coverage >= 70 else "warning" if coverage >= 50 else "danger", unit_total=self.report_data["unit_tests"].get("total", 0), unit_passed=self.report_data["unit_tests"].get("passed", 0), unit_failed=self.report_data["unit_tests"].get("failed", 0), unit_pass_rate=0, integration_total=self.report_data["integration_tests"].get("total", 0), integration_passed=self.report_data["integration_tests"].get("passed", 0), integration_failed=self.report_data["integration_tests"].get("failed", 0), integration_pass_rate=0, e2e_total=self.report_data["e2e_tests"].get("total", 0), e2e_passed=self.report_data["e2e_tests"].get("passed", 0), e2e_failed=self.report_data["e2e_tests"].get("failed", 0), e2e_pass_rate=0, bug_count=len(self.report_data.get("bugs", [])), bug_items=bug_items if bug_items else "
  • 暂无Bug
  • ", timestamp=self.report_data["timestamp"], version=self.report_data["version"] ) report_path = self.report_dir / f"test_report_{self.timestamp}.html" with open(report_path, "w", encoding="utf-8") as f: f.write(html) print(f"✓ HTML报告已生成: {report_path}") return report_path def generate_json_report(self): """生成JSON测试报告""" json_path = self.report_dir / f"test_report_{self.timestamp}.json" with open(json_path, "w", encoding="utf-8") as f: json.dump(self.report_data, f, ensure_ascii=False, indent=2) print(f"✓ JSON报告已生成: {json_path}") return json_path def generate_all_reports(self): """生成所有报告""" print("\n" + "=" * 60) print("🚀 开始生成测试报告...") print("=" * 60) # 运行各类测试 self.run_unit_tests() self.run_integration_tests() self.run_coverage_tests() self.run_security_tests() # 收集Bug self.collect_bugs() # 生成报告 html_report = self.generate_html_report() json_report = self.generate_json_report() print("\n" + "=" * 60) print("✅ 测试报告生成完成!") print("=" * 60) print(f"\n📄 HTML报告: {html_report}") print(f"📄 JSON报告: {json_report}") print(f"📄 覆盖率报告: {self.report_dir}/htmlcov/index.html") print(f"📄 单元测试报告: {self.report_dir}/unit_test_report.html") print(f"📄 集成测试报告: {self.report_dir}/integration_test_report.html") print(f"📄 安全测试报告: {self.report_dir}/security_test_report.html") print("\n" + "=" * 60) if __name__ == "__main__": import sys # 项目根目录 project_root = sys.argv[1] if len(sys.argv) > 1 else "." # 生成测试报告 generator = TestReportGenerator(project_root) generator.generate_all_reports()