import os import subprocess from typing import Optional from pathlib import Path import shutil class ReportHelper: """测试报告辅助工具类""" def __init__( self, allure_results_dir: str = "reports/allure-results", allure_report_dir: str = "reports/allure-report", html_report_dir: str = "reports/html", ): """初始化报告辅助工具 Args: allure_results_dir: Allure测试结果目录 allure_report_dir: Allure报告输出目录 html_report_dir: HTML报告输出目录 """ self.allure_results_dir = allure_results_dir self.allure_report_dir = allure_report_dir self.html_report_dir = html_report_dir self._ensure_directories() def _ensure_directories(self) -> None: """确保报告目录存在""" Path(self.allure_results_dir).mkdir(parents=True, exist_ok=True) Path(self.allure_report_dir).mkdir(parents=True, exist_ok=True) Path(self.html_report_dir).mkdir(parents=True, exist_ok=True) def generate_allure_report(self, clean: bool = True) -> bool: """生成Allure测试报告 Args: clean: 是否清理旧的报告 Returns: 是否生成成功 """ try: if clean and os.path.exists(self.allure_report_dir): shutil.rmtree(self.allure_report_dir) Path(self.allure_report_dir).mkdir(parents=True, exist_ok=True) command = [ "allure", "generate", self.allure_results_dir, "-o", self.allure_report_dir, "--clean", ] result = subprocess.run(command, capture_output=True, text=True) if result.returncode == 0: print(f"Allure报告生成成功: {self.allure_report_dir}") return True else: print(f"Allure报告生成失败: {result.stderr}") return False except FileNotFoundError: print("Allure命令未找到,请先安装Allure: brew install allure") return False except Exception as e: print(f"生成Allure报告时发生错误: {str(e)}") return False def open_allure_report(self) -> bool: """打开Allure测试报告 Returns: 是否打开成功 """ try: if not os.path.exists(self.allure_report_dir): print(f"Allure报告目录不存在: {self.allure_report_dir}") return False command = ["allure", "open", self.allure_report_dir] subprocess.Popen(command) print(f"Allure报告已打开: {self.allure_report_dir}") return True except FileNotFoundError: print("Allure命令未找到,请先安装Allure: brew install allure") return False except Exception as e: print(f"打开Allure报告时发生错误: {str(e)}") return False def serve_allure_report(self, port: int = 8080) -> bool: """启动Allure报告服务器 Args: port: 服务器端口 Returns: 是否启动成功 """ try: if not os.path.exists(self.allure_report_dir): print(f"Allure报告目录不存在: {self.allure_report_dir}") return False command = ["allure", "serve", self.allure_report_dir, "-p", str(port)] subprocess.Popen(command) print(f"Allure报告服务器已启动: http://localhost:{port}") return True except FileNotFoundError: print("Allure命令未找到,请先安装Allure: brew install allure") return False except Exception as e: print(f"启动Allure报告服务器时发生错误: {str(e)}") return False def get_html_report_path(self) -> str: """获取HTML报告路径 Returns: HTML报告路径 """ html_files = list(Path(self.html_report_dir).glob("*.html")) if html_files: return str(html_files[0]) return os.path.join(self.html_report_dir, "report.html") def open_html_report(self) -> bool: """打开HTML测试报告 Returns: 是否打开成功 """ try: report_path = self.get_html_report_path() if not os.path.exists(report_path): print(f"HTML报告文件不存在: {report_path}") return False import webbrowser webbrowser.open(f"file://{os.path.abspath(report_path)}") print(f"HTML报告已打开: {report_path}") return True except Exception as e: print(f"打开HTML报告时发生错误: {str(e)}") return False def clean_allure_results(self) -> bool: """清理Allure测试结果 Returns: 是否清理成功 """ try: if os.path.exists(self.allure_results_dir): shutil.rmtree(self.allure_results_dir) Path(self.allure_results_dir).mkdir(parents=True, exist_ok=True) print(f"Allure测试结果已清理: {self.allure_results_dir}") return True return False except Exception as e: print(f"清理Allure测试结果时发生错误: {str(e)}") return False def clean_allure_report(self) -> bool: """清理Allure报告 Returns: 是否清理成功 """ try: if os.path.exists(self.allure_report_dir): shutil.rmtree(self.allure_report_dir) Path(self.allure_report_dir).mkdir(parents=True, exist_ok=True) print(f"Allure报告已清理: {self.allure_report_dir}") return True return False except Exception as e: print(f"清理Allure报告时发生错误: {str(e)}") return False def clean_html_report(self) -> bool: """清理HTML报告 Returns: 是否清理成功 """ try: if os.path.exists(self.html_report_dir): shutil.rmtree(self.html_report_dir) Path(self.html_report_dir).mkdir(parents=True, exist_ok=True) print(f"HTML报告已清理: {self.html_report_dir}") return True return False except Exception as e: print(f"清理HTML报告时发生错误: {str(e)}") return False def clean_all_reports(self) -> bool: """清理所有报告 Returns: 是否清理成功 """ success = True success = self.clean_allure_results() and success success = self.clean_allure_report() and success success = self.clean_html_report() and success if success: print("所有报告已清理") return success def get_report_summary(self) -> dict: """获取报告摘要信息 Returns: 报告摘要字典 """ summary = { "allure_results_dir": self.allure_results_dir, "allure_report_dir": self.allure_report_dir, "html_report_dir": self.html_report_dir, "allure_results_exists": os.path.exists(self.allure_results_dir), "allure_report_exists": os.path.exists(self.allure_report_dir), "html_report_exists": os.path.exists(self.html_report_dir), "html_report_path": self.get_html_report_path(), } return summary def archive_report(self, archive_name: str, archive_dir: str = "reports/archives") -> bool: """归档报告 Args: archive_name: 归档名称 archive_dir: 归档目录 Returns: 是否归档成功 """ try: Path(archive_dir).mkdir(parents=True, exist_ok=True) timestamp = subprocess.run( ["date", "+%Y%m%d_%H%M%S"], capture_output=True, text=True ).stdout.strip() archive_path = os.path.join(archive_dir, f"{archive_name}_{timestamp}") if os.path.exists(self.allure_report_dir): shutil.copytree(self.allure_report_dir, os.path.join(archive_path, "allure")) if os.path.exists(self.html_report_dir): shutil.copytree(self.html_report_dir, os.path.join(archive_path, "html")) print(f"报告已归档到: {archive_path}") return True except Exception as e: print(f"归档报告时发生错误: {str(e)}") return False def get_allure_history(self) -> list: """获取Allure历史记录 Returns: 历史记录列表 """ history_dir = os.path.join(self.allure_report_dir, "history") if not os.path.exists(history_dir): return [] history = [] for item in os.listdir(history_dir): item_path = os.path.join(history_dir, item) if os.path.isdir(item_path): history.append( { "name": item, "path": item_path, "modified": os.path.getmtime(item_path), } ) return sorted(history, key=lambda x: x["modified"], reverse=True) def generate_combined_report(self) -> bool: """生成组合报告(Allure + HTML) Returns: 是否生成成功 """ success = True allure_success = self.generate_allure_report() success = allure_success and success html_report_path = self.get_html_report_path() if os.path.exists(html_report_path): print(f"HTML报告路径: {html_report_path}") return success def validate_allure_installation(self) -> bool: """验证Allure是否已安装 Returns: Allure是否已安装 """ try: result = subprocess.run(["allure", "--version"], capture_output=True, text=True) if result.returncode == 0: print(f"Allure版本: {result.stdout.strip()}") return True return False except FileNotFoundError: print("Allure未安装") return False except Exception as e: print(f"验证Allure安装时发生错误: {str(e)}") return False def get_test_statistics(self) -> Optional[dict]: """获取测试统计信息(需要Allure已安装) Returns: 测试统计信息字典 """ try: if not self.validate_allure_installation(): return None command = ["allure", "report", "list"] result = subprocess.run(command, capture_output=True, text=True) if result.returncode != 0: return None return { "total": 0, "passed": 0, "failed": 0, "broken": 0, "skipped": 0, "unknown": 0, } except Exception as e: print(f"获取测试统计信息时发生错误: {str(e)}") return None