# @line_count 300 """多格式输出模块""" import json from pathlib import Path from typing import List, Dict, Any, Optional from datetime import datetime try: from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill, Border, Side OPENPYXL_AVAILABLE = True except ImportError: OPENPYXL_AVAILABLE = False class OutputFormatter: """多格式输出格式化器""" def __init__(self, test_items: List[Dict[str, Any]], test_cases: List[Dict[str, Any]], document_info: Optional[Dict[str, Any]] = None): """ 初始化输出格式化器 Args: test_items: 测试项列表 test_cases: 测试用例列表 document_info: 文档信息 """ self.test_items = test_items self.test_cases = test_cases self.document_info = document_info or {} def to_json(self, output_path: Optional[str] = None) -> str: """ 输出为JSON格式 Args: output_path: 输出文件路径,如果为None则返回JSON字符串 Returns: JSON字符串或文件路径 """ data = { 'document_info': self.document_info, 'generation_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'summary': { 'test_item_count': len(self.test_items), 'test_case_count': len(self.test_cases) }, 'test_items': self.test_items, 'test_cases': self.test_cases } json_str = json.dumps(data, ensure_ascii=False, indent=2) if output_path: output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) with open(output_path, 'w', encoding='utf-8') as f: f.write(json_str) return str(output_path) return json_str def to_markdown(self, output_path: Optional[str] = None) -> str: """ 输出为Markdown格式 Args: output_path: 输出文件路径,如果为None则返回Markdown字符串 Returns: Markdown字符串或文件路径 """ lines = [] # 标题和文档信息 lines.append("# 测试项和测试用例文档\n") if self.document_info.get('title'): lines.append(f"**文档标题**: {self.document_info['title']}\n") if self.document_info.get('version'): lines.append(f"**版本**: {self.document_info['version']}\n") lines.append(f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") lines.append(f"**测试项数量**: {len(self.test_items)}\n") lines.append(f"**测试用例数量**: {len(self.test_cases)}\n") lines.append("\n---\n") # 按模块分组 modules = {} for item in self.test_items: module_name = item.get('module_name', '未分类') if module_name not in modules: modules[module_name] = [] modules[module_name].append(item) # 输出每个模块的测试项和测试用例 for module_name, items in modules.items(): lines.append(f"\n## {module_name}\n") for item in items: # 测试项 lines.append(f"\n### {item.get('id', '')} {item.get('name', '')}\n") lines.append(f"- **测试类型**: {item.get('test_type', 'N/A')}") lines.append(f"- **优先级**: {item.get('priority', 'N/A')}") lines.append(f"- **测试目标**: {item.get('test_objective', 'N/A')}\n") # 该测试项下的测试用例 item_cases = [case for case in self.test_cases if case.get('test_item_id') == item.get('id')] if item_cases: lines.append("#### 测试用例\n") for case in item_cases: lines.append(f"\n**{case.get('id', '')} {case.get('name', '')}**\n") lines.append(f"- **前置条件**: {case.get('preconditions', 'N/A')}") lines.append(f"- **优先级**: {case.get('priority', 'N/A')}") lines.append(f"- **测试类型**: {case.get('test_type', 'N/A')}\n") lines.append("**测试步骤**:\n") for idx, step in enumerate(case.get('test_steps', []), 1): lines.append(f"{idx}. {step}\n") lines.append(f"\n**预期结果**: {case.get('expected_result', 'N/A')}\n") lines.append("---\n") markdown_str = '\n'.join(lines) if output_path: output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) with open(output_path, 'w', encoding='utf-8') as f: f.write(markdown_str) return str(output_path) return markdown_str def to_excel(self, output_path: str) -> str: """ 输出为Excel格式 Args: output_path: 输出文件路径 Returns: 文件路径 """ if not OPENPYXL_AVAILABLE: raise ImportError("openpyxl未安装,无法生成Excel文件。请运行: pip install openpyxl") wb = Workbook() # 删除默认工作表 if 'Sheet' in wb.sheetnames: wb.remove(wb['Sheet']) # 创建测试项工作表 self._create_test_items_sheet(wb) # 创建测试用例工作表 self._create_test_cases_sheet(wb) # 创建摘要工作表 self._create_summary_sheet(wb) # 保存文件 output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) wb.save(output_path) return str(output_path) def _create_test_items_sheet(self, wb: Workbook): """创建测试项工作表""" ws = wb.create_sheet("测试项", 0) # 表头 headers = ['测试项ID', '测试项名称', '所属模块', '功能名称', '测试类型', '优先级', '测试目标'] ws.append(headers) # 设置表头样式 header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid") header_font = Font(bold=True, color="FFFFFF") border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) for col in range(1, len(headers) + 1): cell = ws.cell(1, col) cell.fill = header_fill cell.font = header_font cell.border = border cell.alignment = Alignment(horizontal='center', vertical='center') # 数据行 for item in self.test_items: row = [ item.get('id', ''), item.get('name', ''), item.get('module_name', ''), item.get('function_name', ''), item.get('test_type', ''), item.get('priority', ''), item.get('test_objective', '') ] ws.append(row) # 设置边框 for col in range(1, len(headers) + 1): ws.cell(ws.max_row, col).border = border # 调整列宽 column_widths = [12, 30, 15, 15, 12, 8, 40] for col, width in enumerate(column_widths, 1): ws.column_dimensions[ws.cell(1, col).column_letter].width = width # 冻结首行 ws.freeze_panes = 'A2' def _create_test_cases_sheet(self, wb: Workbook): """创建测试用例工作表""" ws = wb.create_sheet("测试用例", 1) # 表头 headers = ['测试用例ID', '测试项ID', '测试用例名称', '所属模块', '前置条件', '测试步骤', '预期结果', '优先级', '测试类型'] ws.append(headers) # 设置表头样式 header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid") header_font = Font(bold=True, color="FFFFFF") border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) for col in range(1, len(headers) + 1): cell = ws.cell(1, col) cell.fill = header_fill cell.font = header_font cell.border = border cell.alignment = Alignment(horizontal='center', vertical='center') # 数据行 for case in self.test_cases: test_steps_str = '\n'.join([f"{idx}. {step}" for idx, step in enumerate(case.get('test_steps', []), 1)]) row = [ case.get('id', ''), case.get('test_item_id', ''), case.get('name', ''), case.get('module_name', ''), case.get('preconditions', ''), test_steps_str, case.get('expected_result', ''), case.get('priority', ''), case.get('test_type', '') ] ws.append(row) # 设置边框和换行 for col in range(1, len(headers) + 1): cell = ws.cell(ws.max_row, col) cell.border = border if col == 6: # 测试步骤列 cell.alignment = Alignment(wrap_text=True, vertical='top') else: cell.alignment = Alignment(vertical='top') # 调整列宽 column_widths = [12, 12, 30, 15, 25, 40, 30, 8, 12] for col, width in enumerate(column_widths, 1): ws.column_dimensions[ws.cell(1, col).column_letter].width = width # 设置行高(为测试步骤列预留空间) for row in range(2, ws.max_row + 1): ws.row_dimensions[row].height = 60 # 冻结首行 ws.freeze_panes = 'A2' def _create_summary_sheet(self, wb: Workbook): """创建摘要工作表""" ws = wb.create_sheet("摘要", 2) # 文档信息 if self.document_info.get('title'): ws.append(['文档标题', self.document_info['title']]) if self.document_info.get('version'): ws.append(['版本', self.document_info['version']]) ws.append(['生成时间', datetime.now().strftime('%Y-%m-%d %H:%M:%S')]) ws.append([]) # 统计信息 ws.append(['统计项', '数量']) ws.append(['测试项总数', len(self.test_items)]) ws.append(['测试用例总数', len(self.test_cases)]) ws.append([]) # 按模块统计 ws.append(['模块', '测试项数量', '测试用例数量']) modules = {} for item in self.test_items: module_name = item.get('module_name', '未分类') if module_name not in modules: modules[module_name] = {'items': 0, 'cases': 0} modules[module_name]['items'] += 1 for case in self.test_cases: module_name = case.get('module_name', '未分类') if module_name not in modules: modules[module_name] = {'items': 0, 'cases': 0} modules[module_name]['cases'] += 1 for module_name, stats in modules.items(): ws.append([module_name, stats['items'], stats['cases']]) # 设置样式 header_font = Font(bold=True) for row in ws.iter_rows(min_row=1, max_row=1): for cell in row: cell.font = header_font # 调整列宽 ws.column_dimensions['A'].width = 20 ws.column_dimensions['B'].width = 15 ws.column_dimensions['C'].width = 15