333 lines
12 KiB
Python
333 lines
12 KiB
Python
# @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
|
||
|