提交 b6ce7d77 authored 作者: 陈泽健's avatar 陈泽健

docs(test-case-generator): 更新测试用例生成需求文档并移除旧代码文件

- 添加 system_type 字段用于区分前后台系统类型
- 更新 module_config.json 格式说明,增加系统类型判断逻辑
- 优化 Page 字段获取说明,强调从当前页面URL提取真实路由
- 明确测试用例不包含登录步骤,假设系统已处于登录状态
- 移除废弃的工具函数模块和浏览器操作模块代码文件
- 更新功能操作策略,明确清理操作步骤需记录到测试用例中
上级 588166f7
......@@ -119,7 +119,8 @@
"mcp__chrome-devtools__click",
"mcp__chrome-devtools__wait_for",
"mcp__chrome-devtools__press_key",
"mcp__chrome-devtools__new_page"
"mcp__chrome-devtools__new_page",
"mcp__chrome-devtools__close_page"
]
}
}
# 自动化测试用例生成工具
通过Claude Code + MCP技术,自动访问系统并生成JSON格式的测试用例。
## 功能特点
- 自动登录系统(支持固定验证码)
- 两级菜单导航
- 智能元素定位(优先ID/Name/Class,备选XPATH)
- 支持添加/编辑/删除操作
- 自动生成JSON测试用例
## 目录结构
```
TestCaseGenerator/
├── main.py # 主程序入口
├── config_manager.py # 配置管理模块
├── browser_operator.py # 浏览器操作模块
├── login_handler.py # 登录处理模块
├── navigation_handler.py # 导航处理模块
├── element_locator.py # 元素定位模块
├── operation_executor.py # 操作执行模块
├── testcase_generator.py # 测试用例生成模块
├── config/ # 配置文件目录
│ ├── system_config.json # 系统配置
│ └── module_config.json # 模块配置
├── testcases/ # 输出测试用例目录
├── utils/ # 工具函数
│ ├── logger.py # 日志工具
│ └── constants.py # 常量定义
└── README.md # 本文件
```
## 配置文件
### system_config.json
系统配置文件,包含登录信息:
```json
[
{
"system_type": "new_platform",
"system_front_url": "https://192.168.5.44",
"system_back_url": "https://192.168.5.44/#/LoginAdmin",
"username": "admin@xty",
"password": "Ubains@4321",
"code": "csba"
}
]
```
### module_config.json
模块配置文件,指定要生成测试用例的模块:
```json
[
{
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加", "编辑", "删除"]
}
]
```
## 使用方法
### 基本用法
```bash
cd AuxiliaryTool/TestCaseGenerator
python main.py
```
### 指定配置文件
```bash
python main.py --system-config config/system_config.json --module-config config/module_config.json
```
### 选择登录类型
```bash
# 使用前台地址登录
python main.py --login-type front
# 使用后台地址登录(默认)
python main.py --login-type back
```
## 输出示例
生成的测试用例文件格式:
```json
{
"name": "区域管理_增值服务_添加",
"para": [
{
"page": "area/valueAddedService",
"step": "点击【添加】按钮",
"locator_type": "ID",
"locator_value": "addButton",
"element_type": "click",
"element_value": "",
"expected_result": ""
},
{
"page": "area/valueAddedService",
"step": "输入服务名称",
"locator_type": "ID",
"locator_value": "serviceName",
"element_type": "input",
"element_value": "测试服务",
"expected_result": ""
},
{
"page": "area/valueAddedService",
"step": "点击【确定】按钮",
"locator_type": "XPATH",
"locator_value": "//span[contains(text(),'确定')]",
"element_type": "click",
"element_value": "",
"expected_result": ""
},
{
"page": "area/valueAddedService",
"step": "获取提示文本",
"locator_type": "CSS_SELECTOR",
"locator_value": ".el-message__content",
"element_type": "getTips",
"element_value": "添加成功",
"expected_result": "添加成功"
}
],
"platform": "web",
"base_url": "https://192.168.5.44/"
}
```
## 支持的元素类型
| element_type | 说明 |
|-------------|------|
| click | 点击按钮、链接等可点击元素 |
| input | 文本输入框 |
| select | 下拉选择框 |
| checkbox | 复选框/单选框 |
| switch | 开关控件 |
| getTips | 获取提示信息 |
## 元素定位策略
按优先级自动选择最优定位方式:
1. **ID属性** - 优先使用唯一ID
2. **Name属性** - 其次使用name属性
3. **Class属性** - 使用class名称
4. **XPATH表达式** - 作为备选方案
5. **CSS选择器** - 作为备选方案
## 注意事项
1. **MCP依赖**:本工具需要chrome-devtools MCP服务运行
2. **验证码**:目前使用固定验证码"csba"
3. **测试数据**:编辑/删除操作会自动创建和清理测试数据
4. **日志文件**:运行日志保存在`logs/`目录下
## 开发规范
- 所有代码包含中文注释
- 符合PEP 8代码规范
- 使用类型注解
- 完善的错误处理和日志记录
## 更新日志
### V1.0 (2026-03-06)
- 初始版本
- 实现核心功能模块
- 支持添加/编辑/删除操作
- 智能元素定位
- JSON测试用例生成
## 相关文档
- 需求文档: `Docs/PRD/自动化测试用例生成/_PRD_自动化测试用例生成需求文档.md`
- 计划执行: `Docs/PRD/自动化测试用例生成/_PRD_自动化测试用例生成需求文档_计划执行.md`
- 代码规范: `Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
[
{
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加", "编辑", "删除"]
}
]
{
"system_type": "后台系统",
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加","编辑","删除"]
},
{
"system_type": "前台系统",
"module_name": "会议室管理",
"module_name_son": "会议室列表",
"module_function": ["添加","编辑"]
}
]
\ No newline at end of file
[
{
{
"system_type": "new_platform",
"system_front_url": "https://192.168.5.44",
"system_back_url": "https://192.168.5.44/#/LoginAdmin",
"username": "admin@xty",
"password": "Ubains@4321",
"code": "csba"
},
{
"system_type": "new_platform",
"system_front_url": "https://192.168.5.44",
"system_back_url": "https://192.168.5.44/#/LoginAdmin",
"back_username": ["XPATH","//input[@placeholder='请输入账号或手机号或邮箱号']","admin@xty"],
"back_password": ["XPATH","//input[@placeholder='请输入密码']","Ubains@4321"],
"back_code": ["XPATH","//input[@placeholder='请输入图形验证码']","csba"],
"front_username": ["XPATH","//input[@placeholder='手机号/用户名/邮箱']","admin@xty"],
"front_password": ["XPATH","//input[@placeholder='密码']","Ubains@4321"],
"front_code": ["XPATH","//input[@placeholder='图形验证']","csba"]
}
]
"system_front_url": "https://192.168.5.44",
"system_back_url": "https://192.168.5.44/#/LoginAdmin",
"username": "admin@xty",
"password": "Ubains@4321",
"code": "csba"
}
]
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
测试用例生成演示脚本
通过chrome-devtools MCP工具演示完整的测试用例生成流程
"""
import json
import time
from pathlib import Path
from typing import Dict, List, Optional
class TestCaseDemo:
"""测试用例生成演示类"""
def __init__(self):
"""初始化演示类"""
self.steps = []
self.current_page = ""
self.current_route = ""
def log(self, message: str):
"""打印日志"""
print(f"[{time.strftime('%H:%M:%S')}] {message}")
def navigate_to_baidu(self):
"""导航到百度首页"""
self.log("步骤1: 导航到百度首页")
result = mcp__chrome_devtools__navigate_page(type="url", url="https://www.baidu.com")
time.sleep(2)
self.current_page = "https://www.baidu.com"
self.log(f"✓ 成功导航到: {self.current_page}")
return True
def take_snapshot_and_analyze(self):
"""获取页面快照并分析"""
self.log("步骤2: 获取页面快照")
snapshot = mcp__chrome_devtools__take_snapshot()
self.log(f"✓ 快照获取成功,元素数量: {len(str(snapshot).split('uid='))}")
return snapshot
def find_search_box(self, snapshot: Dict):
"""查找搜索框元素"""
self.log("步骤3: 查找搜索框元素")
# 在快照中查找搜索框
snapshot_text = str(snapshot)
# 查找搜索框uid
if "uid=1_23 textbox" in snapshot_text:
search_box_uid = "1_23"
self.log(f"✓ 找到搜索框: uid={search_box_uid}")
# 记录步骤
self.add_step(
page="baidu/home",
step="定位搜索框",
locator_type="UID",
locator_value="1_23",
element_type="input",
element_value=""
)
return search_box_uid
self.log("✗ 未找到搜索框")
return None
def fill_search_box(self, uid: str, text: str):
"""填写搜索框"""
self.log(f"步骤4: 填写搜索框,内容: {text}")
# 使用JavaScript填写
script = f"(text) => {{ const input = document.getElementById('kw'); if (input) {{ input.value = text; input.dispatchEvent(new Event('input', {{ bubbles: true }})); return {{success: true, value: text}}; }} return {{success: false}}; }}"
result = mcp__chrome_devtools__evaluate_script(function=script, args=[])
self.log(f"✓ 填写完成: {result}")
# 记录步骤
self.add_step(
page="baidu/home",
step=f"输入搜索内容: {text}",
locator_type="ID",
locator_value="kw",
element_type="input",
element_value=text
)
return True
def find_search_button(self, snapshot: Dict):
"""查找搜索按钮"""
self.log("步骤5: 查找搜索按钮")
snapshot_text = str(snapshot)
if "uid=1_24 button" in snapshot_text and "百度一下" in snapshot_text:
button_uid = "1_24"
self.log(f"✓ 找到搜索按钮: uid={button_uid}")
# 记录步骤
self.add_step(
page="baidu/home",
step="定位搜索按钮",
locator_type="UID",
locator_value="1_24",
element_type="click",
element_value=""
)
return button_uid
self.log("✗ 未找到搜索按钮")
return None
def click_search_button(self, uid: str):
"""点击搜索按钮"""
self.log("步骤6: 点击搜索按钮")
# 使用JavaScript点击
script = "() => { const btn = document.getElementById('su'); if (btn) { btn.click(); return {success: true}; } return {success: false}; }"
result = mcp__chrome_devtools__evaluate_script(function=script, args=[])
self.log(f"✓ 点击完成: {result}")
# 记录步骤
self.add_step(
page="baidu/home",
step="点击【百度一下】按钮",
locator_type="ID",
locator_value="su",
element_type="click",
element_value=""
)
time.sleep(2)
return True
def verify_search_results(self):
"""验证搜索结果页面"""
self.log("步骤7: 验证搜索结果页面")
# 获取当前URL
script = "() => { return window.location.href; }"
result = mcp__chrome_devtools__evaluate_script(function=script, args=[])
self.log(f"✓ 当前URL: {result}")
if "s?" in result or "wd=" in result:
self.log("✓ 成功跳转到搜索结果页面")
# 记录验证步骤
self.add_step(
page="baidu/search",
step="验证搜索结果页面",
locator_type="URL",
locator_value="contains s?",
element_type="getTips",
element_value="搜索成功",
expected_result="跳转到搜索结果页面"
)
return True
return False
def add_step(self, page: str, step: str, locator_type: str,
locator_value: str, element_type: str,
element_value: str, expected_result: str = ""):
"""添加测试步骤"""
self.steps.append({
"page": page,
"step": step,
"locator_type": locator_type,
"locator_value": locator_value,
"element_type": element_type,
"element_value": element_value,
"expected_result": expected_result
})
def generate_testcase(self) -> Dict:
"""生成测试用例"""
self.log("生成测试用例JSON")
testcase = {
"name": "百度_首页_搜索",
"para": self.steps,
"platform": "web",
"base_url": "https://www.baidu.com"
}
return testcase
def save_testcase(self, testcase: Dict, output_path: str):
"""保存测试用例到文件"""
self.log(f"保存测试用例到: {output_path}")
output_file = Path(output_path)
output_file.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(testcase, f, ensure_ascii=False, indent=2)
self.log(f"✓ 测试用例已保存")
def run(self):
"""运行完整演示"""
self.log("=" * 60)
self.log("测试用例生成演示开始")
self.log("=" * 60)
try:
# 1. 导航到百度
if not self.navigate_to_baidu():
return False
# 2. 获取快照
snapshot = self.take_snapshot_and_analyze()
# 3. 查找搜索框
search_box_uid = self.find_search_box(snapshot)
if not search_box_uid:
return False
# 4. 填写搜索框
self.fill_search_box(search_box_uid, "自动化测试")
# 5. 查找搜索按钮
button_uid = self.find_search_button(snapshot)
if not button_uid:
return False
# 6. 点击搜索按钮
self.click_search_button(button_uid)
# 7. 验证搜索结果
self.verify_search_results()
# 8. 生成测试用例
testcase = self.generate_testcase()
# 9. 保存测试用例
output_path = "E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/testcases/百度_首页_搜索.json"
self.save_testcase(testcase, output_path)
# 10. 打印测试用例
self.log("\n生成的测试用例:")
self.log("-" * 60)
print(json.dumps(testcase, ensure_ascii=False, indent=2))
self.log("\n" + "=" * 60)
self.log("演示完成!")
self.log("=" * 60)
return True
except Exception as e:
self.log(f"✗ 演示过程中发生错误: {e}")
return False
def main():
"""主函数"""
demo = TestCaseDemo()
success = demo.run()
return 0 if success else 1
# 此脚本需要在Claude Code环境中运行,以访问MCP工具
# 使用方法:在Claude Code中执行此脚本的内容
此差异已折叠。
# -*- coding: utf-8 -*-
"""
MCP工具包装器模块
提供chrome-devtools MCP工具的调用接口
"""
import json
from typing import Dict, List, Optional, Any
from utils.logger import get_logger
class MCPBrowserWrapper:
"""
MCP浏览器包装器
注意:此类需要通过Claude Code的MCP工具实际调用。
在实际使用时,需要将方法调用传递给Claude Code的MCP工具。
"""
def __init__(self):
"""初始化MCP包装器"""
self.logger = get_logger("MCPBrowserWrapper")
self.page_id: Optional[int] = None
def new_page(self, url: str, background: bool = False,
timeout: int = 30000) -> Optional[Dict]:
"""
打开新页面
Args:
url: 目标URL
background: 是否在后台打开
timeout: 超时时间(毫秒)
Returns:
页面信息字典
"""
self.logger.info(f"[MCP调用] new_page: {url}")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__new_page(url=url, background=background, timeout=timeout)
return {
"pageId": self.page_id,
"url": url
}
def navigate(self, url: str, ignore_cache: bool = False,
timeout: int = 30000) -> bool:
"""
导航到指定URL
Args:
url: 目标URL
ignore_cache: 是否忽略缓存
timeout: 超时时间(毫秒)
Returns:
是否成功
"""
self.logger.info(f"[MCP调用] navigate: {url}")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__navigate_page(type="url", url=url, ignore_cache=ignore_cache)
return True
def take_snapshot(self, verbose: bool = False,
file_path: Optional[str] = None) -> Dict:
"""
获取页面快照
Args:
verbose: 是否包含详细信息
file_path: 快照保存路径
Returns:
页面快照字典
"""
self.logger.debug("[MCP调用] take_snapshot")
# 实际调用需要通过Claude Code的MCP工具
# result = mcp__chrome_devtools__take_snapshot(verbose=verbose, filePath=file_path)
# 返回模拟的快照结构
return {
"elements": [],
"url": "",
"title": ""
}
def click(self, uid: str, dbl_click: bool = False,
include_snapshot: bool = False) -> bool:
"""
点击元素
Args:
uid: 元素uid
dbl_click: 是否双击
include_snapshot: 是否包含快照
Returns:
是否成功
"""
self.logger.debug(f"[MCP调用] click: {uid}")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__click(uid=uid, dblClick=dbl_click, includeSnapshot=include_snapshot)
return True
def fill(self, uid: str, value: str,
include_snapshot: bool = False) -> bool:
"""
填写输入框
Args:
uid: 元素uid
value: 填写值
include_snapshot: 是否包含快照
Returns:
是否成功
"""
self.logger.debug(f"[MCP调用] fill: {uid} = {value}")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__fill(uid=uid, value=value, includeSnapshot=include_snapshot)
return True
def fill_form(self, elements: List[Dict],
include_snapshot: bool = False) -> bool:
"""
批量填写表单
Args:
elements: 元素列表,格式: [{"uid": "xxx", "value": "yyy"}]
include_snapshot: 是否包含快照
Returns:
是否成功
"""
self.logger.debug(f"[MCP调用] fill_form: {len(elements)} elements")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__fill_form(elements=elements, includeSnapshot=include_snapshot)
return True
def evaluate_script(self, function: str,
args: Optional[List[str]] = None) -> Any:
"""
执行JavaScript脚本
Args:
function: JavaScript函数
args: 参数列表(uid列表)
Returns:
脚本执行结果
"""
self.logger.debug(f"[MCP调用] evaluate_script: {function[:50]}...")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__evaluate_script(function=function, args=args or [])
return None
def get_attribute(self, uid: str, attribute: str) -> Optional[str]:
"""
获取元素属性
Args:
uid: 元素uid
attribute: 属性名
Returns:
属性值
"""
self.logger.debug(f"[MCP调用] get_attribute: {uid}.{attribute}")
# 使用evaluate_script获取属性
script = f"(el) => el.getAttribute('{attribute}')"
return self.evaluate_script(script, [uid])
def list_pages(self) -> List[Dict]:
"""
列出所有打开的页面
Returns:
页面列表
"""
self.logger.debug("[MCP调用] list_pages")
# 实际调用需要通过Claude Code的MCP工具
# result = mcp__chrome_devtools__list_pages()
return []
def close_page(self, page_id: int) -> bool:
"""
关闭指定页面
Args:
page_id: 页面ID
Returns:
是否成功
"""
self.logger.info(f"[MCP调用] close_page: {page_id}")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__close_page(pageId=page_id)
return True
def wait_for(self, text: List[str], timeout: int = 30000) -> bool:
"""
等待指定文本出现
Args:
text: 文本列表
timeout: 超时时间(毫秒)
Returns:
是否成功
"""
self.logger.debug(f"[MCP调用] wait_for: {text}")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__wait_for(text=text, timeout=timeout)
return True
def take_screenshot(self, file_path: Optional[str] = None,
format: str = "png", quality: int = 80,
full_page: bool = False) -> bool:
"""
截取屏幕截图
Args:
file_path: 保存路径
format: 图片格式(png/jpeg/webp)
quality: 图片质量(用于jpeg/webp)
full_page: 是否截取整页
Returns:
是否成功
"""
self.logger.debug("[MCP调用] take_screenshot")
# 实际调用需要通过Claude Code的MCP工具
# mcp__chrome_devtools__take_screenshot(filePath=file_path, format=format, quality=quality, fullPage=full_page)
return True
def get_console_messages(self, types: Optional[List[str]] = None,
page_idx: int = 0, page_size: int = 100) -> List[Dict]:
"""
获取控制台消息
Args:
types: 消息类型过滤
page_idx: 页面索引
page_size: 页面大小
Returns:
消息列表
"""
self.logger.debug("[MCP调用] get_console_messages")
# 实际调用需要通过Claude Code的MCP工具
# result = mcp__chrome_devtools__list_console_messages(types=types, pageIdx=page_idx, pageSize=page_size)
return []
# 全局MCP包装器实例(用于跟踪状态)
_mcp_wrapper_instance: Optional[MCPBrowserWrapper] = None
def get_mcp_wrapper() -> MCPBrowserWrapper:
"""获取全局MCP包装器实例"""
global _mcp_wrapper_instance
if _mcp_wrapper_instance is None:
_mcp_wrapper_instance = MCPBrowserWrapper()
return _mcp_wrapper_instance
# -*- coding: utf-8 -*-
"""
导航处理模块
负责处理两级菜单导航
"""
import time
from typing import Dict, Optional, Tuple
from browser_operator import BrowserOperator
from utils.logger import get_logger
from utils.constants import WAIT_SHORT, WAIT_MEDIUM
class NavigationHandler:
"""导航处理器 - 处理菜单导航"""
def __init__(self, browser: BrowserOperator):
"""
初始化导航处理器
Args:
browser: 浏览器操作器实例
"""
self.browser = browser
self.logger = get_logger("NavigationHandler")
def navigate_to_module(self, module_name: str, module_name_son: str) -> Tuple[bool, Optional[str]]:
"""
导航到指定模块(两级菜单)
Args:
module_name: 一级菜单名称
module_name_son: 二级菜单名称
Returns:
(是否成功, 当前路由)
"""
self.logger.info(f"开始导航到模块: {module_name} > {module_name_son}")
# 记录导航前的URL
before_url = self.browser.get_url()
# 1. 查找并展开一级菜单
if not self._expand_first_level_menu(module_name):
self.logger.error(f"展开一级菜单失败: {module_name}")
return False, None
time.sleep(WAIT_SHORT)
# 2. 查找并点击二级菜单
if not self._click_second_level_menu(module_name_son):
self.logger.error(f"点击二级菜单失败: {module_name_son}")
return False, None
# 3. 等待页面加载
time.sleep(WAIT_MEDIUM)
# 4. 获取当前路由
route = self._parse_current_route()
if route:
self.logger.info(f"导航成功,当前路由: {route}")
return True, route
else:
self.logger.warning("未能获取当前路由,但导航可能已成功")
return True, None
def _expand_first_level_menu(self, menu_name: str) -> bool:
"""
展开一级菜单
Args:
menu_name: 一级菜单名称
Returns:
是否成功展开
"""
self.logger.debug(f"正在查找一级菜单: {menu_name}")
snapshot = self.browser.take_snapshot()
# 查找一级菜单
menu_uid = self.browser.find_element_by_text(snapshot, menu_name)
if not menu_uid:
self.logger.error(f"未找到一级菜单: {menu_name}")
return False
self.logger.debug(f"找到一级菜单: {menu_name}, uid: {menu_uid}")
# 点击展开菜单
self.logger.info(f"正在点击一级菜单: {menu_name}")
if not self.browser.click_element(menu_uid):
self.logger.error(f"点击一级菜单失败: {menu_name}")
return False
return True
def _click_second_level_menu(self, submenu_name: str) -> bool:
"""
点击二级菜单
Args:
submenu_name: 二级菜单名称
Returns:
是否成功点击
"""
self.logger.debug(f"正在查找二级菜单: {submenu_name}")
snapshot = self.browser.take_snapshot()
# 查找二级菜单
submenu_uid = self.browser.find_element_by_text(snapshot, submenu_name)
if not submenu_uid:
self.logger.error(f"未找到二级菜单: {submenu_name}")
return False
self.logger.debug(f"找到二级菜单: {submenu_name}, uid: {submenu_uid}")
# 点击二级菜单
self.logger.info(f"正在点击二级菜单: {submenu_name}")
if not self.browser.click_element(submenu_uid):
self.logger.error(f"点击二级菜单失败: {submenu_name}")
return False
return True
def _parse_current_route(self) -> Optional[str]:
"""
解析当前页面的路由
Returns:
路由字符串,如果解析失败返回None
Note:
从URL中提取路由部分,例如:
URL: https://example.com/#/system/user
路由: system/user
"""
current_url = self.browser.get_url()
if not current_url:
self.logger.warning("当前URL为空")
return None
self.logger.debug(f"当前URL: {current_url}")
# 方法1:从hash中提取路由(/#/xxx格式)
if "#/" in current_url:
route = current_url.split("#/")[1]
# 去除查询参数
if "?" in route:
route = route.split("?")[0]
self.logger.debug(f"从hash中提取路由: {route}")
return route
# 方法2:从路径中提取路由
from urllib.parse import urlparse
parsed = urlparse(current_url)
path = parsed.path
# 去除开头的斜杠
if path.startswith("/"):
path = path[1:]
if path:
self.logger.debug(f"从路径中提取路由: {path}")
return path
self.logger.warning("未能从URL中提取路由")
return None
def find_menu_item(self, menu_text: str) -> Optional[str]:
"""
查找菜单项
Args:
menu_text: 菜单文本
Returns:
菜单项的uid,未找到返回None
"""
self.logger.debug(f"正在查找菜单项: {menu_text}")
snapshot = self.browser.take_snapshot()
uid = self.browser.find_element_by_text(snapshot, menu_text)
if uid:
self.logger.debug(f"找到菜单项: {menu_text}")
else:
self.logger.warning(f"未找到菜单项: {menu_text}")
return uid
def get_current_page_info(self) -> Dict:
"""
获取当前页面信息
Returns:
包含URL、路由等信息的字典
"""
current_url = self.browser.get_url()
route = self._parse_current_route()
return {
"url": current_url,
"route": route,
"title": self._get_page_title()
}
def _get_page_title(self) -> Optional[str]:
"""
获取页面标题
Returns:
页面标题
"""
try:
# 使用JavaScript获取页面标题
title = self.browser.execute_script("return document.title;")
return title
except Exception as e:
self.logger.error(f"获取页面标题失败: {e}")
return None
def wait_for_page_load(self, timeout: int = 30) -> bool:
"""
等待页面加载完成
Args:
timeout: 超时时间(秒)
Returns:
是否加载完成
"""
self.logger.debug("等待页面加载完成")
start_time = time.time()
while time.time() - start_time < timeout:
# 检查页面状态
ready_state = self.browser.execute_script("return document.readyState;")
if ready_state == "complete":
self.logger.debug("页面加载完成")
return True
time.sleep(0.5)
self.logger.warning("等待页面加载超时")
return False
def back(self) -> bool:
"""
返回上一页
Returns:
是否成功返回
"""
self.logger.info("正在返回上一页")
# 需要通过MCP工具实现
return True
def refresh(self) -> bool:
"""
刷新当前页面
Returns:
是否成功刷新
"""
self.logger.info("正在刷新页面")
# 需要通过MCP工具实现
return True
# -*- coding: utf-8 -*-
"""
MCP功能测试脚本
验证chrome-devtools MCP工具的基本功能
"""
import json
import time
from pathlib import Path
def test_mcp_basic():
"""测试MCP基本功能"""
print("=" * 50)
print("MCP基本功能测试")
print("=" * 50)
results = []
# 测试1: 列出当前页面
print("\n[测试1] 列出当前页面")
try:
pages = mcp__chrome_devtools__list_pages()
print(f"✓ 成功获取页面列表: {len(pages.get('pages', []))} 个页面")
if pages.get('pages'):
print(f" 当前页面: {pages['pages'][0]}")
results.append(("list_pages", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("list_pages", False, str(e)))
# 测试2: 导航到测试页面
print("\n[测试2] 导航到测试页面")
test_url = "https://www.baidu.com"
try:
result = mcp__chrome_devtools__navigate_page(type="url", url=test_url)
time.sleep(2) # 等待页面加载
print(f"✓ 成功导航到: {test_url}")
results.append(("navigate", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("navigate", False, str(e)))
# 测试3: 获取页面快照
print("\n[测试3] 获取页面快照")
try:
snapshot = mcp__chrome_devtools__take_snapshot()
snapshot_text = str(snapshot)[:200]
print(f"✓ 成功获取快照")
print(f" 快照预览: {snapshot_text}...")
results.append(("take_snapshot", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("take_snapshot", False, str(e)))
# 测试4: 查找搜索框元素
print("\n[测试4] 查找搜索框元素")
try:
snapshot = mcp__chrome_devtools__take_snapshot()
# 百度搜索框的文本
search_keywords = ["百度", "搜索", "百度一下"]
found_uid = None
for keyword in search_keywords:
if keyword in str(snapshot):
print(f"✓ 在快照中找到关键词: {keyword}")
found_uid = "search_box" # 模拟uid
break
if found_uid:
results.append(("find_element", True, f"找到元素: {found_uid}"))
else:
print(f"⚠ 未在快照中找到搜索关键词")
results.append(("find_element", False, "未找到搜索框"))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("find_element", False, str(e)))
# 测试5: 执行JavaScript脚本
print("\n[测试5] 执行JavaScript脚本")
try:
script = "() => { return document.title; }"
result = mcp__chrome_devtools__evaluate_script(function=script)
print(f"✓ 成功执行脚本")
print(f" 页面标题: {result}")
results.append(("evaluate_script", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("evaluate_script", False, str(e)))
# 测试6: 获取当前URL
print("\n[测试6] 获取当前URL")
try:
script = "() => { return window.location.href; }"
result = mcp__chrome_devtools__evaluate_script(function=script)
print(f"✓ 成功获取当前URL: {result}")
results.append(("get_url", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("get_url", False, str(e)))
# 测试7: 截取屏幕截图
print("\n[测试7] 截取屏幕截图")
try:
screenshot_path = "E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/test_screenshot.png"
result = mcp__chrome_devtools__take_screenshot(filePath=screenshot_path, format="png")
print(f"✓ 成功截取屏幕截图")
print(f" 保存路径: {screenshot_path}")
results.append(("take_screenshot", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("take_screenshot", False, str(e)))
# 打印测试结果汇总
print("\n" + "=" * 50)
print("测试结果汇总")
print("=" * 50)
passed = sum(1 for _, success, _ in results if success)
total = len(results)
for test_name, success, error in results:
status = "✓ 通过" if success else "✗ 失败"
print(f"{status:10} {test_name:20} {error if error else ''}")
print(f"\n总计: {passed}/{total} 通过")
return results
def test_mcp_form_interaction():
"""测试MCP表单交互功能"""
print("\n" + "=" * 50)
print("MCP表单交互测试")
print("=" * 50)
results = []
# 导航到百度
print("\n[步骤1] 导航到百度")
try:
mcp__chrome_devtools__navigate_page(type="url", url="https://www.baidu.com")
time.sleep(2)
print("✓ 成功导航到百度")
results.append(("navigate_baidu", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("navigate_baidu", False, str(e)))
return results
# 获取快照并分析
print("\n[步骤2] 获取页面快照")
try:
snapshot = mcp__chrome_devtools__take_snapshot()
print("✓ 成功获取快照")
# 分析快照结构
snapshot_str = str(snapshot)
print(f" 快照长度: {len(snapshot_str)} 字符")
# 查找输入框相关内容
if "input" in snapshot_str.lower() or "textbox" in snapshot_str.lower():
print(" ✓ 快照中包含输入框元素")
results.append(("analyze_snapshot", True, "找到输入框"))
else:
print(" ⚠ 快照中未找到明显的输入框元素")
results.append(("analyze_snapshot", False, "未找到输入框"))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("analyze_snapshot", False, str(e)))
# 尝试填写搜索框(使用JavaScript)
print("\n[步骤3] 尝试填写搜索框")
try:
# 百度搜索框的ID通常是kw
script = """(text) => {
const input = document.getElementById('kw');
if (input) {
input.value = text;
return {success: true, element: 'input#kw'};
}
return {success: false, error: 'input#kw not found'};
}"""
result = mcp__chrome_devtools__evaluate_script(function=script, args=["自动化测试"])
print(f"✓ 成功执行填写脚本")
print(f" 结果: {result}")
results.append(("fill_input", True, str(result)))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("fill_input", False, str(e)))
# 截取填写后的截图
print("\n[步骤4] 截取填写后的页面")
try:
screenshot_path = "E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/test_filled.png"
mcp__chrome_devtools__take_screenshot(filePath=screenshot_path, format="png")
print(f"✓ 成功截取截图: {screenshot_path}")
results.append(("screenshot_filled", True, ""))
except Exception as e:
print(f"✗ 失败: {e}")
results.append(("screenshot_filled", False, str(e)))
return results
def save_test_results(results: list, test_name: str):
"""保存测试结果到文件"""
output_dir = Path("E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/test_results")
output_dir.mkdir(exist_ok=True)
timestamp = time.strftime("%Y%m%d_%H%M%S")
result_file = output_dir / f"{test_name}_{timestamp}.json"
summary = {
"test_name": test_name,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"total_tests": len(results),
"passed_tests": sum(1 for _, success, _ in results if success),
"results": [
{
"test": name,
"success": success,
"error": error
}
for name, success, error in results
]
}
with open(result_file, 'w', encoding='utf-8') as f:
json.dump(summary, f, ensure_ascii=False, indent=2)
print(f"\n测试结果已保存到: {result_file}")
if __name__ == "__main__":
print("开始MCP功能测试...")
# 基本功能测试
basic_results = test_mcp_basic()
save_test_results(basic_results, "mcp_basic_test")
# 表单交互测试
form_results = test_mcp_form_interaction()
save_test_results(form_results, "mcp_form_test")
print("\n" + "=" * 50)
print("所有测试完成!")
print("=" * 50)
# -*- coding: utf-8 -*-
"""
测试用例生成模块
负责根据操作步骤生成JSON格式的测试用例文件
"""
import json
from pathlib import Path
from typing import Dict, List
from datetime import datetime
from utils.logger import get_logger
from utils.constants import EXPECTED_RESULTS
class TestCaseGenerator:
"""测试用例生成器"""
def __init__(self, module_info: Dict, base_url: str):
"""
初始化测试用例生成器
Args:
module_info: 模块信息字典
base_url: 系统基础URL
"""
self.module_info = module_info
self.base_url = base_url
self.logger = get_logger("TestCaseGenerator")
self.module_name = module_info.get("module_name", "")
self.module_name_son = module_info.get("module_name_son", "")
def generate_testcase(self, operation: str, steps: List[Dict]) -> Dict:
"""
生成测试用例
Args:
operation: 操作类型(添加/编辑/删除)
steps: 操作步骤列表
Returns:
测试用例字典
"""
self.logger.info(f"正在生成测试用例: {self.generate_name(operation)}")
testcase = {
"name": self.generate_name(operation),
"para": steps,
"platform": "web",
"base_url": self.base_url
}
# 验证测试用例
if not self._validate_testcase(testcase):
self.logger.error("测试用例验证失败")
return {}
self.logger.info(f"测试用例生成成功,共 {len(steps)} 个步骤")
return testcase
def generate_name(self, operation: str) -> str:
"""
生成测试用例名称
Args:
operation: 操作类型
Returns:
测试用例名称
"""
return f"{self.module_name}_{self.module_name_son}_{operation}"
def generate_expected_result(self, operation: str) -> str:
"""
生成预期结果
Args:
operation: 操作类型
Returns:
预期结果字符串
"""
return EXPECTED_RESULTS.get(operation, f"{operation}成功")
def save_testcase(self, testcase: Dict, output_dir: Path) -> str:
"""
保存测试用例到JSON文件
Args:
testcase: 测试用例字典
output_dir: 输出目录
Returns:
保存的文件路径
"""
if not testcase:
self.logger.error("测试用例为空,无法保存")
return ""
# 确保输出目录存在
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# 生成文件名
filename = f"{testcase['name']}.json"
filepath = output_dir / filename
self.logger.info(f"正在保存测试用例: {filepath}")
try:
# 格式化JSON并保存
json_content = self.format_json(testcase)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(json_content)
self.logger.info(f"测试用例保存成功: {filepath}")
return str(filepath)
except Exception as e:
self.logger.error(f"保存测试用例失败: {e}")
return ""
def format_json(self, data: Dict) -> str:
"""
格式化JSON输出
Args:
data: 要格式化的数据
Returns:
格式化后的JSON字符串
"""
return json.dumps(
data,
ensure_ascii=False,
indent=2,
separators=(',', ': ')
)
def _validate_testcase(self, testcase: Dict) -> bool:
"""
验证测试用例格式
Args:
testcase: 测试用例字典
Returns:
是否验证通过
"""
# 检查必填字段
required_fields = ["name", "para", "platform", "base_url"]
for field in required_fields:
if field not in testcase:
self.logger.error(f"测试用例缺少必填字段: {field}")
return False
# 检查para是否为列表
if not isinstance(testcase["para"], list):
self.logger.error("测试用例的para字段必须是列表")
return False
# 检查每个步骤的必填字段
step_required_fields = ["page", "step", "locator_type", "locator_value", "element_type"]
for idx, step in enumerate(testcase["para"]):
for field in step_required_fields:
if field not in step:
self.logger.warning(f"步骤{idx+1}缺少字段: {field}")
return True
def generate_summary(self, testcases: List[Dict]) -> Dict:
"""
生成测试用例汇总信息
Args:
testcases: 测试用例列表
Returns:
汇总信息字典
"""
summary = {
"module_name": self.module_name,
"module_name_son": self.module_name_son,
"total_count": len(testcases),
"testcases": [tc.get("name", "") for tc in testcases],
"generated_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
return summary
def save_summary(self, summary: Dict, output_dir: Path) -> str:
"""
保存汇总信息
Args:
summary: 汇总信息字典
output_dir: 输出目录
Returns:
保存的文件路径
"""
filename = f"{self.module_name}_{self.module_name_son}_summary.json"
filepath = Path(output_dir) / filename
try:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(self.format_json(summary))
self.logger.info(f"汇总信息保存成功: {filepath}")
return str(filepath)
except Exception as e:
self.logger.error(f"保存汇总信息失败: {e}")
return ""
def batch_generate(self, operations: List[str], steps_dict: Dict[str, List[Dict]],
output_dir: Path) -> List[str]:
"""
批量生成测试用例
Args:
operations: 操作类型列表
steps_dict: 操作步骤字典,key为操作类型,value为步骤列表
output_dir: 输出目录
Returns:
生成的文件路径列表
"""
self.logger.info(f"开始批量生成测试用例,共 {len(operations)} 个操作")
testcases = []
filepaths = []
for operation in operations:
steps = steps_dict.get(operation, [])
if not steps:
self.logger.warning(f"操作 {operation} 没有步骤,跳过")
continue
# 生成测试用例
testcase = self.generate_testcase(operation, steps)
if testcase:
testcases.append(testcase)
# 保存测试用例
filepath = self.save_testcase(testcase, output_dir)
if filepath:
filepaths.append(filepath)
# 生成并保存汇总信息
if testcases:
summary = self.generate_summary(testcases)
self.save_summary(summary, output_dir)
self.logger.info(f"批量生成完成,共生成 {len(filepaths)} 个文件")
return filepaths
# -*- coding: utf-8 -*-
"""
工具函数模块
提供日志、常量等公共功能
"""
from .logger import get_logger
from .constants import *
__all__ = ['get_logger']
# -*- coding: utf-8 -*-
"""
常量定义模块
定义系统中使用的各种常量
"""
# 固定测试数据
TEST_DATA = {
"username": "test001",
"password": "Test@123456",
"phone": "13800138000",
"email": "test001@example.com",
"name": "测试用户",
"description": "测试内容",
"remark": "自动化测试",
"code": "001"
}
# 预期结果映射
EXPECTED_RESULTS = {
"添加": "添加成功",
"编辑": "编辑成功",
"删除": "删除成功",
"提交": "提交成功",
"保存": "保存成功",
"修改": "修改成功"
}
# 支持的元素类型
ELEMENT_TYPES = {
# 基础类型
"click": "点击按钮、链接等可点击元素",
"input": "文本输入框",
"select": "下拉选择框",
# 选择类型
"checkbox": "复选框/单选框",
"switch": "开关控件",
# 验证类型
"getTips": "获取弹窗提示信息(如Element UI的el-message组件)",
"getText": "获取列表数据文本(如表格td元素中的文本)"
}
# element_type 与 element_value 填写规则映射
ELEMENT_VALUE_RULES = {
"input": "填写需输入的文本内容",
"getTips": "留空(验证内容填写在expected_result中)",
"getText": "留空(验证内容填写在expected_result中)",
"click": "留空",
"select": "留空",
"checkbox": "留空",
"switch": "留空"
}
# 定位策略优先级(注意:不允许使用UID)
LOCATOR_PRIORITIES = [
"ID", # 优先级1:ID属性
"NAME", # 优先级2:Name属性
"CLASS", # 优先级3:Class属性
"XPATH", # 备选:XPATH表达式
"CSS_SELECTOR" # 备选:CSS选择器
]
# 禁止使用的定位类型
FORBIDDEN_LOCATOR_TYPES = ["UID"]
# XPath规范
XPATH_CONTAINS_PATTERN = "contains(.,'{text}')" # 使用 . 而非 text()
XPATH_CONTAINS_OLD_PATTERN = "contains(text(),'{text}')" # 旧的错误模式
# 定位策略优先级
LOCATOR_PRIORITIES = [
"ID", # 优先级1:ID属性
"NAME", # 优先级2:Name属性
"CLASS", # 优先级3:Class属性
"XPATH", # 备选:XPATH表达式
"CSS_SELECTOR" # 备选:CSS选择器
]
# 等待超时时间(秒)
WAIT_TIMEOUT = 30
WAIT_SHORT = 5
WAIT_MEDIUM = 10
# MCP相关常量
MCP_TIMEOUT = 60000 # MCP操作超时时间(毫秒)
# 浏览器相关
BROWSER_WIDTH = 1920
BROWSER_HEIGHT = 1080
# 文件相关
CONFIG_DIR = "config"
TESTCASES_DIR = "testcases"
LOGS_DIR = "logs"
# -*- coding: utf-8 -*-
"""
日志工具模块
提供统一的日志输出功能
"""
import logging
import sys
from pathlib import Path
from datetime import datetime
def get_logger(name: str = "TestCaseGenerator", level: int = logging.INFO) -> logging.Logger:
"""
获取日志记录器
Args:
name: 日志记录器名称
level: 日志级别
Returns:
配置好的日志记录器
"""
logger = logging.getLogger(name)
# 避免重复添加handler
if logger.handlers:
return logger
logger.setLevel(level)
# 控制台输出handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
# 日志格式
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
return logger
def setup_file_logger(logger: logging.Logger, log_dir: Path) -> None:
"""
设置文件日志输出
Args:
logger: 日志记录器
log_dir: 日志文件目录
"""
log_dir.mkdir(parents=True, exist_ok=True)
# 日志文件名:按日期命名
log_file = log_dir / f"testcase_generator_{datetime.now().strftime('%Y%m%d')}.log"
# 文件输出handler
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
# 文件日志格式
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
......@@ -34,17 +34,20 @@
```json
[
{
"system_type": "后台系统",
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加","编辑","删除"]
},
{
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加","编辑","删除"]
"system_type": "前台系统",
"module_name": "会议室管理",
"module_name_son": "会议室列表",
"module_function": ["添加","编辑"]
}
]
```
```
- **注意**:`system_type`字段用于判断使用前台还是后台URL进行登录
3. 通过claude code+mcp,自动访问系统进行模块操作,并通过自动化测试用例JSON数据模板输出自动化测试用例。
- 自动化测试用例JSON数据模板格式示例:
......@@ -150,7 +153,7 @@
- 参数说明:
- name: 测试用例名称,命名格式为:{module_name}_{module_name_son}_{model_function},例如:区域管理_增值服务_添加。
- para: 测试步骤列表,每个步骤包含以下字段:
- page: page字段为当前模块功能操作的页面路由后缀。
- page: **当前模块功能操作的页面真实路由后缀**(非固定值),通过URL解析获取,例如:`"areaManagement/valueAddedService"`
- step: 操作步骤描述,例如:点击左侧【用户管理】。
- locator_type: 元素定位类型,例如:ID、XPATH、CSS_SELECTOR等。优先获取ID、XPATH,**不允许用UID**。
- locator_value: 元素定位值,例如://button[contains(.,'确定')],需要与locator_type一致。
......@@ -180,7 +183,9 @@
- `password`: 登录密码
- `code`: 验证码(固定值:csba)
**注意**:登录时需用户输入选择使用前台或后台URL。
**注意**:根据`module_config.json`中的`system_type`字段判断使用前台还是后台URL:
- `system_type`包含"后台""admin" 使用`system_back_url`
- `system_type`包含"前台"或其他 使用`system_front_url`
#### 3.1.2 module_config.json格式
- `module_name`: 一级菜单名称
......@@ -194,10 +199,16 @@
1. 先点击一级菜单(`module_name`)
2. 再点击二级菜单(`module_name_son`)
**注意**:导航到指定模块的步骤**必须记录到测试用例的`para`中**,作为测试步骤的前置操作。
#### 3.2.2 功能操作策略
- **添加操作**:填写通用固定测试值
- **编辑操作**:先自动创建测试数据 执行编辑 清理测试数据
- **删除操作**:先自动创建测试数据 执行删除 清理测试数据
- **编辑操作**:先自动创建测试数据 执行编辑 **清理测试数据**
- **注意**:清理操作步骤**需要记录到测试用例的`para`中**
- 如果清理失败,则记录提示步骤并标注"跳过清理"
- **删除操作**:先自动创建测试数据 执行删除 **清理测试数据**
- **注意**:清理操作步骤**需要记录到测试用例的`para`中**
- 如果清理失败,则记录提示步骤并标注"跳过清理"
#### 3.2.3 元素定位策略
采用**智能检测**方式,按以下优先级选择定位方式:
......@@ -209,12 +220,22 @@
#### 3.2.4 Page字段获取
通过**URL解析**获取当前功能所在页面的路由后缀。
**注意**:
- `page`字段**不是固定值**,必须从当前页面URL中提取真实路由
- 例如:当前URL为`https://192.168.5.44/#/areaManagement/valueAddedService`,则`page`字段应填写为`"areaManagement/valueAddedService"`
#### 3.2.5 预期结果生成
根据操作类型**自动生成**预期结果提示文本,例如:
- 添加操作 "添加成功"
- 编辑操作 "编辑成功"
- 删除操作 "删除成功"
#### 3.2.6 登录步骤处理
**重要**:生成的测试用例**不包含登录步骤**。
- 假设测试执行时系统已处于登录状态
- 登录操作由测试框架在执行测试用例前统一处理
- 测试用例直接从导航到目标模块开始记录
### 3.3 支持的元素类型(element_type)
#### 3.3.1 基础类型
......
# 操作手册 - 如何生成新模块的自动化测试用例
## 前置条件
### 1. 确保Chrome DevTools MCP工具已启动
```bash
# 检查MCP服务是否正常运行
# 确保 Claude Code 可以访问 chrome-devtools MCP 工具
```
### 2. 准备配置文件
确保以下配置文件已正确配置:
- `AuxiliaryTool/TestCaseGenerator/config/system_config.json` - 系统登录配置
- `AuxiliaryTool/TestCaseGenerator/config/module_config.json` - 模块配置
---
## 操作步骤
### 使用Claude Code对话式生成
#### Step 1: 更新模块配置文件
编辑 `module_config.json`,添加新模块配置:
```json
[
{
"system_type": "后台系统",
"module_name": "一级菜单名称",
"module_name_son": "二级菜单名称",
"module_function": ["添加", "编辑", "删除"]
}
]
```
**配置说明:**
- `system_type`: 包含"后台"或"admin"使用后台URL,否则使用前台URL
- `module_name`: 一级菜单名称(如:区域管理、用户管理)
- `module_name_son`: 二级菜单名称(如:增值服务、用户列表)
- `module_function`: 需要生成的功能类型,支持:
- `添加` - 新增数据操作
- `编辑` - 修改数据操作
- `删除` - 删除数据操作
#### Step 2: 在Claude Code对话中输入指令
```
请根据module_config.json和system_config.json生成自动化测试JSON数据
```
#### Step 3: Claude Code自动执行流程
Claude Code会通过MCP工具自动执行以下流程:
1. 打开浏览器并登录系统
2. 导航到指定模块(点击一级菜单 > 二级菜单)
3. 执行配置的功能操作(添加/编辑/删除)
4. 收集元素定位信息(使用XPATH等方式)
5. 生成测试用例JSON文件
#### Step 4: 查看生成的文件
生成的文件位于:
```
AuxiliaryTool/TestCaseGenerator/testcases/{模块名}_{子模块名}_{功能}.json
```
---
## 配置文件路径
**重要提示:** 目前配置文件位于以下位置,请确保路径正确:
```
AuxiliaryTool/TestCaseGenerator/
├── config/
│ ├── system_config.json # 系统登录配置
│ └── module_config.json # 模块配置(编辑此文件添加新模块)
└── testcases/ # 生成的测试用例输出目录
```
**如果config目录不存在,请先创建:**
```bash
mkdir -p "E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/config"
```
---
## 配置文件详解
### system_config.json 格式
```json
[
{
"system_type": "new_platform",
"system_front_url": "https://192.168.5.44",
"system_back_url": "https://192.168.5.44/#/LoginAdmin",
"username": "admin@xty",
"password": "Ubains@4321",
"code": "csba"
}
]
```
| 字段 | 说明 | 示例值 |
|------|------|--------|
| system_type | 系统类型标识 | "new_platform" |
| system_front_url | 前台地址 | "https://192.168.5.44" |
| system_back_url | 后台地址 | "https://192.168.5.44/#/LoginAdmin" |
| username | 登录账号 | "admin@xty" |
| password | 登录密码 | "Ubains@4321" |
| code | 验证码(固定值) | "csba" |
### module_config.json 格式
```json
[
{
"system_type": "后台系统",
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加", "编辑", "删除"]
},
{
"system_type": "前台系统",
"module_name": "会议室管理",
"module_name_son": "会议室列表",
"module_function": ["添加", "编辑"]
}
]
```
**注意:** 可以一次配置多个模块,程序会依次处理。
---
## 生成的测试用例JSON格式说明
### 完整结构
```json
{
"name": "{一级菜单}_{二级菜单}_{功能}",
"para": [
{
"page": "页面路由后缀",
"step": "操作步骤描述",
"locator_type": "定位类型",
"locator_value": "定位值",
"element_type": "元素类型",
"element_value": "操作值",
"expected_result": "预期结果"
}
],
"platform": "web",
"base_url": "系统基础URL"
}
```
### 字段说明
#### element_type 类型说明
| element_type | 说明 | element_value 填写 | expected_result 填写 |
|---------------|------|---------------------|----------------------|
| click | 点击按钮/链接 | 留空 `""` | 通常留空 |
| input | 文本输入框 | 填写输入内容 | 通常留空 |
| select | 下拉选择框 | 留空 `""` | 通常留空 |
| checkbox | 复选框/单选框 | 留空 `""` | 通常留空 |
| switch | 开关控件 | 留空 `""` | 通常留空 |
| getTips | 获取弹窗提示 | **留空** `""` | 填写预期提示文本 |
| getText | 获取列表文本 | **留空** `""` | 填写验证描述 |
#### locator_type 优先级
1. **ID** - 使用id属性(优先)
2. **NAME** - 使用name属性
3. **CLASS** - 使用class属性
4. **XPATH** - 使用XPATH表达式(备选)
5. **CSS_SELECTOR** - 使用CSS选择器(备选)
**注意:** 不允许使用UID作为定位类型。
---
## 常见问题处理
### Q1: 浏览器已运行错误
**错误信息:** `The browser is already running...`
**解决方案:**
这个错误通常不影响Claude Code执行,因为Claude Code会自动连接到现有的浏览器实例。如果出现问题,可以:
```bash
# Windows - 关闭所有Chrome进程(谨慎使用)
taskkill /F /IM chrome.exe
```
然后重新启动Claude Code对话。
### Q2: 登录失败
**可能原因:**
- 网络不通,无法访问系统地址
- 账号密码错误
- 验证码已更改
**解决方案:**
- 检查网络连接
- 更新system_config.json中的账号密码
- 确认验证码是否为"csba"
### Q3: 找不到菜单元素
**可能原因:**
- 菜单名称配置错误
- 页面加载未完成
**解决方案:**
- 确认module_name和module_name_son与实际页面完全一致
- 增加等待时间(修改utils/constants.py中的WAIT_TIMEOUT)
### Q4: 元素定位失败
**可能原因:**
- 页面结构变化
- 元素在iframe中
**解决方案:**
- 使用Chrome DevTools检查页面结构
- 考虑使用iframe处理
---
## 输出目录结构
```
AuxiliaryTool/TestCaseGenerator/
├── config/
│ ├── system_config.json # 系统配置(登录信息)
│ └── module_config.json # 模块配置(要生成的模块)
└── testcases/ # 生成的测试用例输出目录
├── 区域管理_增值服务_添加.json
├── 区域管理_增值服务_编辑.json
└── 区域管理_增值服务_删除.json
```
**注意:** 如果config目录不存在,请先创建:
```bash
mkdir -p "E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/config"
```
---
## 扩展:批量生成多个模块
### 一次生成多个模块的配置示例
```json
[
{
"system_type": "后台系统",
"module_name": "区域管理",
"module_name_son": "增值服务",
"module_function": ["添加", "编辑", "删除"]
},
{
"system_type": "后台系统",
"module_name": "区域管理",
"module_name_son": "区域列表",
"module_function": ["添加", "编辑", "删除"]
},
{
"system_type": "后台系统",
"module_name": "用户管理",
"module_name_son": "用户列表",
"module_function": ["添加", "编辑"]
}
]
```
程序会依次处理每个模块配置,生成对应的测试用例文件。
---
## 注意事项
1. **数据清理**:编辑和删除操作会创建测试数据,测试完成后需要手动清理
2. **页面等待**:如果网络较慢,可能需要增加等待时间
3. **编码问题**:确保JSON文件使用UTF-8编码保存
4. **浏览器版本**:建议使用最新版Chrome浏览器
---
## 附录:快速开始模板
### Step 1: 确保配置文件存在
**创建config目录(如果不存在):**
```bash
mkdir -p "E:/GithubData/ubains-module-test/AuxiliaryTool/TestCaseGenerator/config"
```
**system_config.json(系统登录配置):**
```json
[{
"system_type": "new_platform",
"system_front_url": "https://192.168.5.44",
"system_back_url": "https://192.168.5.44/#/LoginAdmin",
"username": "admin@xty",
"password": "Ubains@4321",
"code": "csba"
}]
```
**module_config.json(模块配置):**
```json
[{
"system_type": "后台系统",
"module_name": "一级菜单",
"module_name_son": "二级菜单",
"module_function": ["添加"]
}]
```
### Step 2: 在Claude Code中执行
直接输入指令:
```
请根据module_config.json和system_config.json生成自动化测试JSON数据
```
### Step 3: 查看结果
生成的文件位于:
```
AuxiliaryTool/TestCaseGenerator/testcases/
```
---
## 总结
**目前唯一支持的方式:** 通过Claude Code对话式生成
**核心优势:**
- 无需编写代码
- 自动处理浏览器操作
- 智能识别元素定位
- 自动生成标准JSON格式
**下次使用时:** 只需更新 `module_config.json`,然后在Claude Code中输入指令即可!
---
*文档版本:v1.1*
*更新日期:2026-03-06*
*更新说明:移除命令行方式,仅保留Claude Code对话式生成方式*
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论