#!/usr/bin/env python3 """ 测试套件执行报告生成器 用途: - 统计测试用例数量 - 分析测试覆盖率 - 生成测试执行摘要 - 输出测试报告 """ import os import sys import json import subprocess from pathlib import Path from datetime import datetime from typing import Dict, List, Any class TestReportGenerator: """测试报告生成器""" def __init__(self, test_suite_path: str): self.test_suite_path = Path(test_suite_path) self.tests_path = self.test_suite_path / "tests" self.report_data = { "generated_at": datetime.now().isoformat(), "test_suites": {}, "summary": { "total_test_files": 0, "total_test_cases": 0, "test_categories": {} } } def count_test_cases(self, file_path: Path) -> int: """统计测试文件中的测试用例数量""" try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # 统计以async def test_或def test_开头的函数 count = content.count('async def test_') + content.count('def test_') return count except Exception as e: print(f"Error reading {file_path}: {e}") return 0 def analyze_test_directory(self, dir_path: Path, category: str) -> Dict[str, Any]: """分析测试目录""" test_files = list(dir_path.glob("test_*.py")) category_data = { "test_files": [], "total_files": len(test_files), "total_cases": 0 } for test_file in test_files: case_count = self.count_test_cases(test_file) file_info = { "file_name": test_file.name, "relative_path": str(test_file.relative_to(self.tests_path)), "test_cases": case_count } category_data["test_files"].append(file_info) category_data["total_cases"] += case_count return category_data def generate_report(self) -> Dict[str, Any]: """生成测试报告""" print("正在分析测试套件...") # 分析各个测试目录 test_categories = { "unit": self.tests_path / "unit", "integration": self.tests_path / "integration", "e2e": self.tests_path / "e2e", "uat": self.tests_path / "uat", "performance": self.tests_path / "performance", "security": self.tests_path / "security" } for category, path in test_categories.items(): if path.exists(): print(f"分析 {category} 测试...") category_data = self.analyze_test_directory(path, category) self.report_data["test_suites"][category] = category_data # 更新汇总信息 self.report_data["summary"]["total_test_files"] += category_data["total_files"] self.report_data["summary"]["total_test_cases"] += category_data["total_cases"] self.report_data["summary"]["test_categories"][category] = { "files": category_data["total_files"], "cases": category_data["total_cases"] } return self.report_data def print_report(self): """打印测试报告""" print("\n" + "="*60) print(" Novalon后台管理系统 - 测试套件执行报告") print("="*60) print(f"\n生成时间: {self.report_data['generated_at']}") print("\n" + "-"*60) print(" 测试套件统计") print("-"*60) for category, data in self.report_data["test_suites"].items(): print(f"\n{category.upper()} 测试:") print(f" 测试文件数: {data['total_files']}") print(f" 测试用例数: {data['total_cases']}") if data['test_files']: print(f" 测试文件列表:") for file_info in data['test_files']: print(f" - {file_info['file_name']}: {file_info['test_cases']} 个用例") print("\n" + "-"*60) print(" 汇总信息") print("-"*60) print(f"\n总测试文件数: {self.report_data['summary']['total_test_files']}") print(f"总测试用例数: {self.report_data['summary']['total_test_cases']}") print("\n测试分类统计:") for category, stats in self.report_data["summary"]["test_categories"].items(): print(f" {category}: {stats['files']} 文件, {stats['cases']} 用例") print("\n" + "="*60) def save_report(self, output_file: str): """保存测试报告到文件""" output_path = self.test_suite_path / output_file with open(output_path, 'w', encoding='utf-8') as f: json.dump(self.report_data, f, indent=2, ensure_ascii=False) print(f"\n测试报告已保存到: {output_path}") def generate_markdown_report(self, output_file: str): """生成Markdown格式的测试报告""" output_path = self.test_suite_path / output_file with open(output_path, 'w', encoding='utf-8') as f: f.write("# Novalon后台管理系统 - 测试套件执行报告\n\n") f.write(f"**生成时间**: {self.report_data['generated_at']}\n\n") f.write("## 测试套件统计\n\n") for category, data in self.report_data["test_suites"].items(): f.write(f"### {category.upper()} 测试\n\n") f.write(f"- **测试文件数**: {data['total_files']}\n") f.write(f"- **测试用例数**: {data['total_cases']}\n\n") if data['test_files']: f.write("**测试文件列表**:\n\n") for file_info in data['test_files']: f.write(f"- `{file_info['file_name']}`: {file_info['test_cases']} 个用例\n") f.write("\n") f.write("## 汇总信息\n\n") f.write(f"- **总测试文件数**: {self.report_data['summary']['total_test_files']}\n") f.write(f"- **总测试用例数**: {self.report_data['summary']['total_test_cases']}\n\n") f.write("### 测试分类统计\n\n") f.write("| 测试类型 | 文件数 | 用例数 |\n") f.write("|---------|--------|--------|\n") for category, stats in self.report_data["summary"]["test_categories"].items(): f.write(f"| {category} | {stats['files']} | {stats['cases']} |\n") f.write("\n## 测试执行建议\n\n") f.write("### 快速测试\n") f.write("```bash\n") f.write("./run_tests.sh integration -v\n") f.write("```\n\n") f.write("### 完整测试\n") f.write("```bash\n") f.write("./run_tests.sh all -v\n") f.write("```\n\n") f.write("### UAT验收测试\n") f.write("```bash\n") f.write("./run_uat_tests.sh all -v\n") f.write("```\n\n") f.write("## 测试报告查看\n\n") f.write("### 查看覆盖率报告\n") f.write("```bash\n") f.write("open htmlcov/all/index.html\n") f.write("```\n\n") f.write("### 查看Allure报告\n") f.write("```bash\n") f.write("allure serve allure-results/all\n") f.write("```\n") print(f"Markdown报告已保存到: {output_path}") def main(): """主函数""" # 获取测试套件路径 script_path = Path(__file__).parent test_suite_path = script_path # 创建报告生成器 generator = TestReportGenerator(str(test_suite_path)) # 生成报告 generator.generate_report() # 打印报告 generator.print_report() # 保存JSON报告 generator.save_report("test_suite_report.json") # 生成Markdown报告 generator.generate_markdown_report("TEST_SUITE_REPORT.md") print("\n✅ 测试套件报告生成完成!") if __name__ == "__main__": main()