229 lines
8.3 KiB
Python
Executable File
229 lines
8.3 KiB
Python
Executable File
#!/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()
|