report_generator.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import json
  2. import jinja2
  3. import pandas as pd
  4. from datetime import datetime
  5. from pathlib import Path
  6. from core.config.config_manager import ConfigManager
  7. from core.utils.logger import Logger
  8. from core.singleton import Singleton
  9. class ReportGenerator(metaclass=Singleton):
  10. def __init__(self):
  11. self.config = ConfigManager()
  12. self.logger = Logger.get_logger()
  13. self.template_loader = jinja2.FileSystemLoader(
  14. searchpath=Path(__file__).parent.parent.parent / 'resources' / 'templates'
  15. )
  16. self.template_env = jinja2.Environment(loader=self.template_loader)
  17. self.report_dir = Path(__file__).parent.parent.parent / 'resources' / 'reports'
  18. self.report_dir.mkdir(exist_ok=True)
  19. def generate_test_report(self, test_results, report_type='html', report_name=None):
  20. """生成测试报告"""
  21. if report_type == 'html':
  22. return self.generate_html_report(test_results, report_name)
  23. elif report_type == 'json':
  24. return self.generate_json_report(test_results, report_name)
  25. elif report_type == 'excel':
  26. return self.generate_excel_report(test_results, report_name)
  27. else:
  28. raise ValueError(f"不支持的报告类型: {report_type}")
  29. def generate_html_report(self, test_results, report_name=None):
  30. """生成HTML报告"""
  31. try:
  32. template = self.template_env.get_template("report_template.html")
  33. # 准备报告数据
  34. report_data = self._prepare_report_data(test_results)
  35. # 渲染模板
  36. html_content = template.render(report_data)
  37. # 生成报告文件名
  38. if not report_name:
  39. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  40. report_name = f"test_report_{timestamp}.html"
  41. # 保存报告
  42. report_path = self.report_dir / report_name
  43. with open(report_path, "w", encoding="utf-8") as f:
  44. f.write(html_content)
  45. self.logger.info(f"HTML报告已生成: {report_path}")
  46. return report_path
  47. except Exception as e:
  48. self.logger.error(f"生成HTML报告失败: {str(e)}")
  49. raise
  50. def generate_json_report(self, test_results, report_name=None):
  51. """生成JSON报告"""
  52. try:
  53. # 准备报告数据
  54. report_data = self._prepare_report_data(test_results)
  55. # 生成报告文件名
  56. if not report_name:
  57. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  58. report_name = f"test_report_{timestamp}.json"
  59. # 保存报告
  60. report_path = self.report_dir / report_name
  61. with open(report_path, "w", encoding="utf-8") as f:
  62. json.dump(report_data, f, indent=4, ensure_ascii=False)
  63. self.logger.info(f"JSON报告已生成: {report_path}")
  64. return report_path
  65. except Exception as e:
  66. self.logger.error(f"生成JSON报告失败: {str(e)}")
  67. raise
  68. def generate_excel_report(self, test_results, report_name=None):
  69. """生成Excel报告"""
  70. try:
  71. # 准备数据
  72. df_data = []
  73. for result in test_results:
  74. df_data.append({
  75. '测试用例': result['name'],
  76. '状态': result['status'],
  77. '执行时间(秒)': result['duration'],
  78. '开始时间': result['start_time'],
  79. '结束时间': result['end_time'],
  80. '错误信息': result.get('error', ''),
  81. '截图': result.get('screenshot', '')
  82. })
  83. df = pd.DataFrame(df_data)
  84. # 生成报告文件名
  85. if not report_name:
  86. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  87. report_name = f"test_report_{timestamp}.xlsx"
  88. # 保存报告
  89. report_path = self.report_dir / report_name
  90. with pd.ExcelWriter(report_path, engine='openpyxl') as writer:
  91. df.to_excel(writer, sheet_name='测试结果', index=False)
  92. # 添加摘要信息
  93. summary_data = {
  94. '指标': ['总测试数', '通过数', '失败数', '通过率', '总执行时间'],
  95. '值': [
  96. len(test_results),
  97. sum(1 for r in test_results if r['status'] == 'PASS'),
  98. sum(1 for r in test_results if r['status'] == 'FAIL'),
  99. f"{(sum(1 for r in test_results if r['status'] == 'PASS') / len(test_results)) * 100 if test_results else 0:.2f}%",
  100. f"{sum(r['duration'] for r in test_results):.2f}秒"
  101. ]
  102. }
  103. summary_df = pd.DataFrame(summary_data)
  104. summary_df.to_excel(writer, sheet_name='测试摘要', index=False)
  105. self.logger.info(f"Excel报告已生成: {report_path}")
  106. return report_path
  107. except Exception as e:
  108. self.logger.error(f"生成Excel报告失败: {str(e)}")
  109. raise
  110. def _prepare_report_data(self, test_results):
  111. """准备报告数据"""
  112. total_duration = sum(r['duration'] for r in test_results)
  113. passed_count = sum(1 for r in test_results if r['status'] == 'PASS')
  114. failed_count = sum(1 for r in test_results if r['status'] == 'FAIL')
  115. success_rate = (passed_count / len(test_results)) * 100 if test_results else 0
  116. # 按模块分组
  117. modules = {}
  118. for result in test_results:
  119. module_name = result.get('module', '其他')
  120. if module_name not in modules:
  121. modules[module_name] = {
  122. 'total': 0,
  123. 'passed': 0,
  124. 'failed': 0,
  125. 'tests': []
  126. }
  127. modules[module_name]['total'] += 1
  128. if result['status'] == 'PASS':
  129. modules[module_name]['passed'] += 1
  130. else:
  131. modules[module_name]['failed'] += 1
  132. modules[module_name]['tests'].append(result)
  133. return {
  134. "title": "自动化测试报告",
  135. "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  136. "results": test_results,
  137. "modules": modules,
  138. "total_tests": len(test_results),
  139. "passed": passed_count,
  140. "failed": failed_count,
  141. "success_rate": f"{success_rate:.2f}",
  142. "total_duration": f"{total_duration:.2f}",
  143. "environment": self.config.get('environment', '未知环境')
  144. }
  145. def send_report_by_email(self, report_paths, recipients=None):
  146. """通过邮件发送报告"""
  147. from utils.report.email_notifier import EmailNotifier
  148. email_notifier = EmailNotifier()
  149. if not recipients:
  150. recipients = self.config.get('report.email_recipients', [])
  151. if not recipients:
  152. self.logger.warning("没有配置邮件接收人,跳过发送邮件")
  153. return
  154. subject = f"自动化测试报告 - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
  155. body = f"自动化测试执行完成,请查看附件中的测试报告。\n\n执行环境: {self.config.get('environment', '未知环境')}"
  156. try:
  157. email_notifier.send_email(
  158. recipients=recipients,
  159. subject=subject,
  160. body=body,
  161. attachments=report_paths
  162. )
  163. self.logger.info("测试报告已通过邮件发送")
  164. except Exception as e:
  165. self.logger.error(f"发送邮件失败: {str(e)}")