This commit is contained in:
2026-02-04 14:38:52 +08:00
commit a5147b1429
29 changed files with 4489 additions and 0 deletions

459
modules/test_generator.py Normal file
View File

@@ -0,0 +1,459 @@
# @line_count 250
"""测试生成引擎模块"""
import json
import re
from typing import List, Dict, Any, Optional, Callable
from .json_parser import JSONParser
from .api_client import APIClient
from .prompt_manager import PromptManager
class TestGenerator:
"""测试项和测试用例生成器"""
def __init__(self, json_path: str, api_client: Optional[APIClient] = None,
prompt_manager: Optional[PromptManager] = None):
"""
初始化测试生成器
Args:
json_path: JSON文档路径
api_client: API客户端实例
prompt_manager: Prompt管理器实例
"""
self.parser = JSONParser(json_path)
self.api_client = api_client or APIClient()
self.prompt_manager = prompt_manager or PromptManager()
self.test_items: List[Dict[str, Any]] = []
self.test_cases: List[Dict[str, Any]] = []
self.test_case_counter = 0
def generate_test_items(self, function_points: Optional[List[Dict[str, Any]]] = None,
progress_callback: Optional[Callable] = None) -> List[Dict[str, Any]]:
"""
生成测试项
Args:
function_points: 功能点列表如果为None则自动提取
progress_callback: 进度回调函数 (current, total, message)
Returns:
测试项列表
"""
if function_points is None:
function_points = self.parser.extract_function_points()
test_items = []
total = len(function_points)
for idx, func_point in enumerate(function_points):
if progress_callback:
progress_callback(idx + 1, total, f"正在生成测试项: {func_point['function_name']}")
try:
# 构建功能描述
function_description = self._build_function_description(func_point)
# 格式化prompt
prompt = self.prompt_manager.format_prompt(
'test_item',
function_description=function_description,
module_name=func_point['module_name']
)
# 调用API
response = self.api_client.call_api(prompt)
# 解析响应
test_items_for_func = self._parse_test_items_response(response, func_point)
test_items.extend(test_items_for_func)
except Exception as e:
# 如果生成失败,创建一个默认测试项
test_items.append({
'id': f"TI-{len(test_items) + 1:03d}",
'name': f"{func_point['function_name']}功能测试",
'module_name': func_point['module_name'],
'function_name': func_point['function_name'],
'test_type': '功能测试',
'priority': '',
'test_objective': f"测试{func_point['function_name']}功能是否正常工作",
'error': str(e)
})
self.test_items = test_items
return test_items
def generate_test_cases(self, test_items: Optional[List[Dict[str, Any]]] = None,
progress_callback: Optional[Callable] = None) -> List[Dict[str, Any]]:
"""
为测试项生成测试用例
Args:
test_items: 测试项列表如果为None则使用已生成的测试项
progress_callback: 进度回调函数
Returns:
测试用例列表
"""
if test_items is None:
test_items = self.test_items
if not test_items:
raise ValueError("没有可用的测试项,请先生成测试项")
test_cases = []
total = len(test_items)
for idx, test_item in enumerate(test_items):
if progress_callback:
progress_callback(idx + 1, total, f"正在生成测试用例: {test_item['name']}")
try:
# 获取功能点信息
function_points = self.parser.extract_function_points()
func_point = next(
(fp for fp in function_points if fp['function_name'] == test_item.get('function_name', '')),
None
)
if not func_point:
continue
function_description = self._build_function_description(func_point)
# 格式化prompt
prompt = self.prompt_manager.format_prompt(
'test_case',
test_item_name=test_item['name'],
test_type=test_item.get('test_type', '功能测试'),
module_name=test_item['module_name'],
function_description=function_description
)
# 调用API
response = self.api_client.call_api(prompt)
# 解析响应
cases_for_item = self._parse_test_cases_response(response, test_item)
test_cases.extend(cases_for_item)
except Exception as e:
# 如果生成失败,创建一个默认测试用例
self.test_case_counter += 1
test_cases.append({
'id': f"TC-{self.test_case_counter:03d}",
'test_item_id': test_item.get('id', ''),
'name': f"{test_item['name']} - 基础功能验证",
'module_name': test_item['module_name'],
'preconditions': '系统正常运行',
'test_steps': ['步骤1打开功能模块', '步骤2执行操作', '步骤3验证结果'],
'expected_result': '功能正常工作',
'priority': test_item.get('priority', ''),
'test_type': test_item.get('test_type', '功能测试'),
'error': str(e)
})
self.test_cases = test_cases
return test_cases
def generate_batch(self, function_points: Optional[List[Dict[str, Any]]] = None,
progress_callback: Optional[Callable] = None) -> Dict[str, List[Dict[str, Any]]]:
"""
批量生成测试项和测试用例(一次性生成)
Args:
function_points: 功能点列表
progress_callback: 进度回调函数
Returns:
包含test_items和test_cases的字典
"""
if function_points is None:
function_points = self.parser.extract_function_points()
all_test_items = []
all_test_cases = []
total = len(function_points)
self.test_case_counter = 0
for idx, func_point in enumerate(function_points):
if progress_callback:
progress_callback(idx + 1, total, f"正在生成: {func_point['function_name']}")
try:
# 转换为requirement格式如果使用规范化Prompt
requirement = self._convert_to_requirement(func_point)
# 尝试使用规范化Prompt如果支持
if self.prompt_manager.use_standards:
prompt = self.prompt_manager.format_prompt(
'batch',
requirement=requirement
)
else:
# 使用传统方式
function_description = self._build_function_description(func_point)
prompt = self.prompt_manager.format_prompt(
'batch',
function_description=function_description,
module_name=func_point['module_name']
)
# 调用API
response = self.api_client.call_api(prompt)
# 解析批量响应
result = self._parse_batch_response(response, func_point)
all_test_items.extend(result['test_items'])
all_test_cases.extend(result['test_cases'])
except Exception as e:
# 创建默认测试项和测试用例
item_id = f"TI-{len(all_test_items) + 1:03d}"
all_test_items.append({
'id': item_id,
'name': f"{func_point['function_name']}功能测试",
'module_name': func_point['module_name'],
'function_name': func_point['function_name'],
'test_type': '功能测试',
'priority': '',
'test_objective': f"测试{func_point['function_name']}功能",
'error': str(e)
})
self.test_case_counter += 1
all_test_cases.append({
'id': f"TC-{self.test_case_counter:03d}",
'test_item_id': item_id,
'name': f"{func_point['function_name']} - 基础验证",
'module_name': func_point['module_name'],
'preconditions': '系统正常运行',
'test_steps': ['执行功能操作', '验证结果'],
'expected_result': '功能正常',
'priority': '',
'test_type': '功能测试',
'error': str(e)
})
self.test_items = all_test_items
self.test_cases = all_test_cases
return {
'test_items': all_test_items,
'test_cases': all_test_cases
}
def _build_function_description(self, func_point: Dict[str, Any]) -> str:
"""构建功能描述文本"""
parts = []
if func_point.get('module_description'):
parts.append(f"模块描述:{func_point['module_description']}")
if func_point.get('description'):
parts.append(f"功能描述:{func_point['description']}")
if func_point.get('operation_steps'):
parts.append(f"操作步骤:{''.join(func_point['operation_steps'])}")
return '\n'.join(parts) if parts else func_point.get('function_name', '')
def _parse_test_items_response(self, response: str, func_point: Dict[str, Any]) -> List[Dict[str, Any]]:
"""解析测试项API响应"""
try:
# 尝试提取JSON
json_str = self._extract_json_from_response(response)
data = json.loads(json_str)
test_items = []
for idx, item in enumerate(data.get('test_items', [])):
test_items.append({
'id': f"TI-{len(self.test_items) + len(test_items) + 1:03d}",
'name': item.get('name', ''),
'module_name': func_point['module_name'],
'function_name': func_point['function_name'],
'test_type': item.get('test_type', '功能测试'),
'priority': item.get('priority', ''),
'test_objective': item.get('test_objective', '')
})
return test_items if test_items else [self._create_default_test_item(func_point)]
except Exception:
return [self._create_default_test_item(func_point)]
def _parse_test_cases_response(self, response: str, test_item: Dict[str, Any]) -> List[Dict[str, Any]]:
"""解析测试用例API响应"""
try:
json_str = self._extract_json_from_response(response)
data = json.loads(json_str)
test_cases = []
for case in data.get('test_cases', []):
self.test_case_counter += 1
test_cases.append({
'id': f"TC-{self.test_case_counter:03d}",
'test_item_id': test_item.get('id', ''),
'name': case.get('name', ''),
'module_name': test_item['module_name'],
'preconditions': case.get('preconditions', ''),
'test_steps': case.get('test_steps', []),
'expected_result': case.get('expected_result', ''),
'priority': case.get('priority', test_item.get('priority', '')),
'test_type': case.get('test_type', test_item.get('test_type', '功能测试'))
})
return test_cases if test_cases else [self._create_default_test_case(test_item)]
except Exception:
return [self._create_default_test_case(test_item)]
def _parse_batch_response(self, response: str, func_point: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]:
"""解析批量生成API响应"""
try:
print("\n" + "-"*60)
print("[解析响应] 开始解析API响应...")
print(f"原始响应长度: {len(response)} 字符")
json_str = self._extract_json_from_response(response)
print(f"提取的JSON长度: {len(json_str)} 字符")
data = json.loads(json_str)
print(f"JSON解析成功")
print(f" - test_items数量: {len(data.get('test_items', []))}")
test_items = []
test_cases = []
for item_data in data.get('test_items', []):
item_id = f"TI-{len(self.test_items) + len(test_items) + 1:03d}"
test_item = {
'id': item_id,
'name': item_data.get('name', ''),
'module_name': func_point['module_name'],
'function_name': func_point['function_name'],
'test_type': item_data.get('test_type', '功能测试'),
'priority': item_data.get('priority', ''),
'test_objective': item_data.get('test_objective', '')
}
test_items.append(test_item)
print(f" ✓ 测试项: {test_item['name']}")
# 处理该测试项下的测试用例
cases_in_item = item_data.get('test_cases', [])
print(f" - 测试用例数量: {len(cases_in_item)}")
for case_data in cases_in_item:
self.test_case_counter += 1
test_cases.append({
'id': f"TC-{self.test_case_counter:03d}",
'test_item_id': item_id,
'name': case_data.get('name', ''),
'module_name': func_point['module_name'],
'preconditions': case_data.get('preconditions', ''),
'test_steps': case_data.get('test_steps', []),
'expected_result': case_data.get('expected_result', ''),
'priority': case_data.get('priority', test_item['priority']),
'test_type': case_data.get('test_type', test_item['test_type'])
})
if not test_items:
print(" ⚠️ 没有解析到test_items使用默认值")
test_item = self._create_default_test_item(func_point)
test_items.append(test_item)
test_cases.append(self._create_default_test_case(test_item))
print(f"[解析响应] 完成!共 {len(test_items)} 个测试项,{len(test_cases)} 个测试用例")
print("-"*60 + "\n")
return {
'test_items': test_items,
'test_cases': test_cases
}
except Exception as e:
print("\n" + "-"*60)
print(f"[解析响应] ❌ 解析失败!错误: {str(e)}")
print(f"原始响应前500字符: {response[:500]}")
print("-"*60 + "\n")
test_item = self._create_default_test_item(func_point)
return {
'test_items': [test_item],
'test_cases': [self._create_default_test_case(test_item)]
}
def _extract_json_from_response(self, response: str) -> str:
"""从API响应中提取JSON字符串"""
# 尝试直接解析
try:
json.loads(response)
return response
except:
pass
# 尝试提取代码块中的JSON
json_pattern = r'```(?:json)?\s*(\{.*?\})\s*```'
match = re.search(json_pattern, response, re.DOTALL)
if match:
return match.group(1)
# 尝试提取大括号中的内容
brace_match = re.search(r'\{.*\}', response, re.DOTALL)
if brace_match:
return brace_match.group(0)
raise ValueError("无法从响应中提取JSON")
def _create_default_test_item(self, func_point: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认测试项"""
return {
'id': f"TI-{len(self.test_items) + 1:03d}",
'name': f"{func_point['function_name']}功能测试",
'module_name': func_point['module_name'],
'function_name': func_point['function_name'],
'test_type': '功能测试',
'priority': '',
'test_objective': f"测试{func_point['function_name']}功能是否正常工作"
}
def _create_default_test_case(self, test_item: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认测试用例"""
self.test_case_counter += 1
return {
'id': f"TC-{self.test_case_counter:03d}",
'test_item_id': test_item.get('id', ''),
'name': f"{test_item['name']} - 基础功能验证",
'module_name': test_item['module_name'],
'preconditions': '系统正常运行',
'test_steps': ['步骤1打开功能模块', '步骤2执行操作', '步骤3验证结果'],
'expected_result': '功能正常工作',
'priority': test_item.get('priority', ''),
'test_type': test_item.get('test_type', '功能测试')
}
def _convert_to_requirement(self, func_point: Dict[str, Any]) -> Dict[str, Any]:
"""
将功能点转换为需求格式用于规范化Prompt
Args:
func_point: 功能点字典
Returns:
需求字典
"""
requirement = {
'requirement_id': func_point.get('requirement_id', func_point.get('function_name', '')),
'requirement_type': func_point.get('requirement_type', '功能需求'),
'description': func_point.get('description', func_point.get('function_name', '')),
'module_name': func_point.get('module_name', '')
}
# 如果有接口信息,添加进去
if func_point.get('interface_info'):
requirement['interface_info'] = func_point['interface_info']
return requirement