Files
test_item_gen/modules/test_generator.py
2026-02-04 14:38:52 +08:00

460 lines
19 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @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