+ [misc] Add enhanced ZenTao UI test guide with branch workflow requirements

- Add prominent warnings and branch workflow requirements at document start
- Include comprehensive step-by-step UI test development process
- Add branch naming convention: uitest/{ModuleName}/{action}/{AIModel}
- Include detailed git commands for branch management and code submission
- Add UI test development checklist with mandatory and quality check items
- Ensure UI test scripts follow same branch development standards as unit tests
- Fix trailing whitespace issues in documentation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
liugang
2025-08-22 23:47:13 +08:00
co-authored by Claude
parent 57dba596b9
commit 641be8eb03
+851
View File
@@ -0,0 +1,851 @@
# ZenTao UI自动化测试脚本生成指导文档
## 概述
本文档用于指导大模型为ZenTao项目生成高质量的UI自动化测试脚本。ZenTao的UI自动化测试基于WebDriver技术,采用页面对象模式,支持多浏览器测试和丰富的断言机制。
---
**🚨🚨🚨 重要警告 🚨🚨🚨**
**在开始编写任何代码之前,必须先创建测试分支!**
**任何直接在开发分支上编写UI测试的行为都是严重违规!**
**必须严格按照分支→开发→测试→提交→推送的流程执行!**
---
## ⚠️ 重要:分支开发流程(必须执行)
**任何UI测试脚本的开发都必须严格按照以下分支流程进行:**
### 第一步:创建测试分支(必须)
1. **检查当前分支**:确认当前处于开发分支(通常是release分支或master分支)
2. **创建测试分支**:基于当前开发分支创建新的测试分支
3. **分支命名规范**`uitest/{ModuleName}/{action}/{大模型名称}`
- 示例:`uitest/user/create/claude`
4. **切换到测试分支**:立即切换到新创建的测试分支进行开发
### 分支操作命令示例:
```bash
# 检查当前分支
git branch
# 创建并切换到UI测试分支
git checkout -b uitest/user/create/claude
# 确认已切换到测试分支
git branch
```
**⚠️ 关键要求:**
- 分支名必须包含模块名、功能、大模型标识
- 例如:`uitest/user/create/claude`
- 如果忘记创建分支,必须立即停止当前操作并创建分支
**🚨 警告:如果不创建测试分支,将违反ZenTao开发规范,必须立即停止操作并创建正确的分支!**
## UI测试框架架构
### 1. 目录结构规范
```
module/{moduleName}/test/
├── lib/ # UI测试类库
│ └── {action}.ui.class.php # UI测试类
├── ui/ # UI测试脚本
│ ├── {action}.php # 测试执行脚本
│ └── page/ # 页面对象
│ └── {action}.php # 页面元素定义
└── yaml/ # UI测试数据(继承单元测试YAML)
```
### 2. 核心类继承关系
```
tester (基础测试类)
└── {moduleName}Tester (模块测试类)
└── createUserTester (具体功能测试类)
```
## 核心文件类型详解
### 1. UI测试执行脚本 ({action}.php)
**文件头格式:**
```php
#!/usr/bin/env php
<?php
/**
title={功能描述}
timeout=0
cid=1
- 测试场景1描述
- 测试结果 @期望结果1
- 测试场景2描述
- 测试结果 @期望结果2
- 测试场景3描述
- 测试结果 @期望结果3
*/
```
**标准代码结构:**
```php
// 1. 切换到脚本目录
chdir(__DIR__);
// 2. 导入UI测试类
include '../lib/{action}.ui.class.php';
// 3. 使用zendata准备测试数据
zendata('{tableName}')->loadYaml('{yamlName}', false, 2)->gen(10);
// 或者直接使用zenData API
$table = zenData('{tableName}');
$table->{field}->range('{range}');
$table->gen({count});
// 4. 创建测试实例并登录
$tester = new {action}Tester();
$tester->login();
// 5. 准备测试数据对象
${objectName} = new stdclass();
${objectName}->field1 = 'value1';
${objectName}->field2 = 'value2';
${objectName}->verifyPassword = $config->uitest->defaultPassword;
// 6. 执行测试用例
r($tester->{testMethod}(${objectName})) && p('message') && e('期望结果1'); // 测试场景1
r($tester->{testMethod}(${objectName})) && p('message') && e('期望结果2'); // 测试场景2
// 7. 清理资源
$tester->closeBrowser();
```
### 2. UI测试类 ({action}.ui.class.php)
**类结构模板:**
```php
<?php
include dirname(__FILE__, 5) . '/test/lib/ui.php';
class {action}Tester extends tester
{
/**
* {功能描述}
*
* @param object ${objectName}
* @access public
* @return object
*/
public function {methodName}(${objectName})
{
// 1. 初始化表单
$form = $this->initForm('{moduleName}', '{action}', array(), 'appIframe-admin');
// 2. 填写表单字段
$form->dom->{fieldName}->setValue(${objectName}->{fieldName});
$form->dom->{fieldName2}->setValue(${objectName}->{fieldName2});
// 3. 提交表单
$form->dom->btn($this->lang->save)->click();
$form->wait(1);
// 4. 验证结果
if($this->response('method') != '{expectedMethod}')
{
if($this->checkFormTips('{moduleName}')) return $this->success('{successMessage}');
return $this->failed('{failMessage}');
}
return $this->success('{operationSuccessMessage}');
}
}
```
### 3. 页面对象文件 (page/{action}.php)
**页面元素定义:**
```php
<?php
class {action}Page
{
// 页面元素定位器
public static $account = "#account";
public static $password = "#password";
public static $realname = "#realname";
public static $submit = "input[type='submit']";
// 页面URL配置
public static $url = "/{moduleName}-{action}";
// 页面标题
public static $title = "{页面标题}";
}
```
## 核心API详解
### 1. 表单操作API
**initForm() - 初始化表单**
```php
$form = $this->initForm($moduleName, $action, $params, $frameId);
```
- `$moduleName`: 模块名称
- `$action`: 操作名称
- `$params`: URL参数数组
- `$frameId`: iframe ID(如'appIframe-admin'
**表单字段操作**
```php
$form->dom->{fieldName}->setValue($value); // 设置字段值
$form->dom->{fieldName}->getText(); // 获取字段文本
$form->dom->{fieldName}->click(); // 点击元素
$form->dom->{fieldName}->clear(); // 清空字段
```
**按钮操作**
```php
$form->dom->btn($this->lang->save)->click(); // 点击保存按钮
$form->dom->btn('提交')->click(); // 点击指定文本按钮
```
### 2. 页面跳转验证
**response() - 获取响应信息**
```php
$this->response('method'); // 获取当前方法
$this->response('module'); // 获取当前模块
$this->response('url'); // 获取当前URL
```
**页面跳转验证模式**
```php
// 模式1:验证跳转到指定页面
if($this->response('method') != 'browse')
{
// 未跳转,检查错误提示
if($this->checkFormTips('{moduleName}')) return $this->success('错误提示正确');
return $this->failed('错误提示不正确');
}
return $this->success('操作成功');
// 模式2:直接验证操作结果
if($this->checkFormTips('{moduleName}')) return $this->success('验证成功');
return $this->failed('验证失败');
```
### 3. 等待和同步机制
**等待方法**
```php
$form->wait(1); // 等待1秒
$this->waitFor('#elementId'); // 等待元素出现
$this->waitForText('期望文本'); // 等待文本出现
```
### 4. 断言和验证
**checkFormTips() - 检查表单提示**
```php
if($this->checkFormTips('{moduleName}'))
{
// 有错误提示
return $this->success('错误提示正确');
}
```
**返回结果方法**
```php
return $this->success($message); // 测试成功
return $this->failed($message); // 测试失败
```
## 测试场景设计原则
### 1. 正常业务流程测试
- 用户正常操作流程
- 表单正确填写和提交
- 页面正常跳转验证
### 2. 表单验证测试
- 必填字段为空
- 数据格式错误
- 业务规则违反(如重复数据)
- 权限验证
### 3. 用户交互测试
- 按钮点击响应
- 下拉框选择
- 复选框/单选框操作
- 文件上传功能
### 4. 页面状态测试
- 页面加载完成
- 数据显示正确
- 错误信息显示
- 成功提示显示
## 常见UI测试模式
### 1. 创建操作测试
```php
public function createNormalItem($item)
{
$form = $this->initForm('product', 'create', array(), 'appIframe-admin');
$form->dom->name->setValue($item->name);
$form->dom->code->setValue($item->code);
$form->dom->desc->setValue($item->desc);
$form->dom->verifyPassword->setValue($item->verifyPassword);
$form->dom->btn($this->lang->save)->click();
$form->wait(1);
if($this->response('method') != 'browse')
{
if($this->checkFormTips('product')) return $this->success('创建失败提示正确');
return $this->failed('创建失败提示错误');
}
return $this->success('创建成功');
}
```
### 2. 编辑操作测试
```php
public function editItem($item)
{
$form = $this->initForm('product', 'edit', array('productID' => 1));
$form->dom->name->clear();
$form->dom->name->setValue($item->name);
$form->dom->verifyPassword->setValue($item->verifyPassword);
$form->dom->btn($this->lang->save)->click();
$form->wait(1);
return $this->success('编辑成功');
}
```
### 3. 批量操作测试
```php
public function batchCreateItems($items)
{
$form = $this->initForm('user', 'batchCreate', array());
foreach($items as $index => $item)
{
$form->dom->{"account[$index]"}->setValue($item->account);
$form->dom->{"realname[$index]"}->setValue($item->realname);
}
$form->dom->verifyPassword->setValue($items[0]->verifyPassword);
$form->dom->btn($this->lang->save)->click();
$form->wait(2);
return $this->success('批量创建成功');
}
```
### 4. 列表页面测试
```php
public function browsePage()
{
$this->openUrl('product', 'browse');
// 验证列表数据显示
if($this->checkElementExists('.table tbody tr'))
{
return $this->success('列表显示正常');
}
return $this->failed('列表显示异常');
}
```
## 高级功能使用
### 1. iframe操作
```php
// 切换到iframe
$this->switchToFrame('appIframe-admin');
// 在iframe中操作
$form = $this->initForm('user', 'create', array(), 'appIframe-admin');
// 切换回主框架
$this->switchToDefaultFrame();
```
### 2. 弹窗处理
```php
// 处理确认弹窗
$this->acceptAlert();
// 处理取消弹窗
$this->dismissAlert();
// 获取弹窗文本
$alertText = $this->getAlertText();
```
### 3. 多选框和下拉框
```php
// 下拉框选择
$form->dom->select->selectByText('选项文本');
$form->dom->select->selectByValue('optionValue');
// 多选框操作
$form->dom->checkbox->check(); // 选中
$form->dom->checkbox->uncheck(); // 取消选中
```
### 4. 文件上传
```php
// 文件上传
$form->dom->fileInput->sendKeys('/path/to/file');
```
## zendata测试数据管理
ZenTao的UI测试同样使用zendata工具进行测试数据管理,支持灵活的数据生成和配置。
### 1. zendata数据准备方式
**方式一:直接使用zenData API**
```php
// 基础数据准备
$userTable = zenData('user');
$userTable->account->range('admin,user{99},test{50}');
$userTable->password->range('123456{100},Test123{50}');
$userTable->realname->range('管理员,用户{99},测试{50}');
$userTable->role->range('admin{1},qa{20},dev{50},pm{10}');
$userTable->gen(10);
// 多表关联数据
$productTable = zenData('product');
$productTable->name->range('产品{10}');
$productTable->code->range('PROD{10}');
$productTable->gen(5);
```
**方式二:加载YAML配置文件**
```php
// 从YAML文件加载配置
zendata('user')->loadYaml('user', false, 2)->gen(10);
// 加载后覆盖特定字段
$table = zenData('story');
$table->loadYaml('story_create', false, 1);
$table->title->range('需求{100}');
$table->status->range('draft{30},active{50},closed{20}');
$table->gen(20);
```
**方式三:混合使用**
```php
// 先加载基础数据,再添加特殊测试数据
zendata('user')->loadYaml('user', false, 2)->gen(8);
// 添加特殊测试用户
$specialUser = zenData('user');
$specialUser->id->range('999');
$specialUser->account->range('specialtest');
$specialUser->password->range('Special@123');
$specialUser->gen(1);
```
### 2. UI测试中的数据对象创建
**标准数据对象创建:**
```php
// 正常测试数据
$user = new stdclass();
$user->account = 'testuser' . time(); // 避免重复
$user->password = 'Test123456!';
$user->confirmPassword = 'Test123456!';
$user->realname = '测试用户';
$user->email = 'test@example.com';
$user->verifyPassword = $config->uitest->defaultPassword;
// 边界值测试数据
$emptyUser = new stdclass();
$emptyUser->account = '';
$emptyUser->password = '';
$emptyUser->realname = '';
$emptyUser->verifyPassword = $config->uitest->defaultPassword;
// 重复数据测试
$duplicateUser = new stdclass();
$duplicateUser->account = 'admin'; // 已存在的用户名
$duplicateUser->password = 'Admin123';
$duplicateUser->verifyPassword = $config->uitest->defaultPassword;
```
**动态数据生成:**
```php
// 使用时间戳确保唯一性
$timestamp = time();
$product = new stdclass();
$product->name = "测试产品_{$timestamp}";
$product->code = "PROD_{$timestamp}";
$product->desc = "这是一个测试产品的描述信息";
// 使用随机数生成测试数据
$randomId = mt_rand(10000, 99999);
$project = new stdclass();
$project->name = "项目_" . $randomId;
$project->code = "PRJ_" . $randomId;
```
### 3. zendata在不同测试场景中的应用
**创建功能测试:**
```php
// 准备干净的测试环境
zendata('user')->gen(0); // 清空用户表
// 生成基础测试数据
$baseUsers = zenData('user');
$baseUsers->account->range('admin,manager');
$baseUsers->password->range('123456{2}');
$baseUsers->gen(2);
// 测试对象
$newUser = new stdclass();
$newUser->account = 'newuser001';
$newUser->password = 'NewUser123!';
```
**批量操作测试:**
```php
// 生成批量测试数据
$batchTable = zenData('user');
$batchTable->account->range('[]{5}'); // 5个空账号用于批量创建
$batchTable->gen(5);
// 批量创建的数据数组
$batchUsers = [];
for($i = 0; $i < 5; $i++) {
$user = new stdclass();
$user->account = "batch_user_$i";
$user->realname = "批量用户$i";
$user->password = "Batch123!";
$batchUsers[] = $user;
}
```
**权限测试:**
```php
// 准备不同角色的用户
$roleTable = zenData('user');
$roleTable->account->range('admin,manager,developer,tester,guest');
$roleTable->role->range('admin,pm,dev,qa,limited');
$roleTable->gen(5);
// 准备权限组数据
$groupTable = zenData('usergroup');
$groupTable->account->range('admin,manager,developer,tester,guest');
$groupTable->group->range('1,2,3,4,5');
$groupTable->gen(5);
```
### 4. zendata数据清理和重置
**测试前数据准备:**
```php
// 清空相关表数据
zendata('user')->gen(0);
zendata('product')->gen(0);
zendata('project')->gen(0);
// 重新生成基础数据
zendata('user')->loadYaml('user_basic', false, 1)->gen(5);
zendata('product')->loadYaml('product_basic', false, 1)->gen(3);
```
**测试间数据隔离:**
```php
class createProductTester extends tester
{
public function setUp()
{
// 每个测试方法前重置数据
zendata('product')->gen(0);
zendata('user')->loadYaml('user', false, 1)->gen(5);
}
public function tearDown()
{
// 测试后清理(可选)
// zendata('product')->gen(0);
}
}
```
### 5. zendata配置文件在UI测试中的最佳实践
**模块化YAML配置:**
```
test/yaml/
├── user_basic.yaml # 基础用户数据
├── user_roles.yaml # 角色权限数据
├── product_basic.yaml # 基础产品数据
├── project_basic.yaml # 基础项目数据
└── story_workflow.yaml # 需求工作流数据
```
**测试特定的数据配置:**
```yaml
# test/yaml/user_ui_test.yaml
---
title: UI测试用户数据配置
fields:
- field: account
range: uitest{1},testuser{5},[]{3} # UI测试专用账号+空值测试
- field: password
range: UITest123!{6},[]{3} # 统一密码+空密码测试
- field: realname
range: UI测试用户{6},[]{3} # 中文名称+空名称测试
- field: email
range: uitest@test.com{6},[]{3} # 测试邮箱+空邮箱测试
```
**使用示例:**
```php
// 加载UI测试专用配置
zendata('user')->loadYaml('user_ui_test', false, 2)->gen(10);
// 验证管理员账号存在
$adminTable = zenData('user');
$adminTable->id->range('1');
$adminTable->account->range('admin');
$adminTable->password->range('123456');
$adminTable->role->range('admin');
$adminTable->gen(1);
```
通过合理使用zendata,可以确保UI测试数据的一致性、可重复性和完整性,提高测试的稳定性和可维护性。
## 错误处理和调试
### 1. 错误捕获
```php
try {
$form->dom->element->click();
} catch (Exception $e) {
return $this->failed('元素点击失败: ' . $e->getMessage());
}
```
### 2. 截图保存
```php
// 失败时自动截图
if($testFailed) {
$this->screenshot('test_failure_' . date('Y-m-d_H-i-s'));
}
```
### 3. 调试信息输出
```php
// 输出调试信息
echo "Current URL: " . $this->response('url') . "\n";
echo "Page Title: " . $this->getTitle() . "\n";
```
## 最佳实践
### 1. 测试脚本组织
- 一个功能一个测试类
- 相关的测试方法放在同一个类中
- 测试方法命名要描述清楚测试场景
### 2. 页面元素定位
- 优先使用ID选择器
- 避免使用易变的class名称
- 使用稳定的属性进行定位
- 建立页面对象模式
### 3. 等待策略
- 显式等待优于隐式等待
- 等待时间要合理,不要过长
- 关键操作后要有适当等待
- 页面跳转后要等待加载完成
### 4. 测试数据管理
- 每个测试用例独立准备数据
- 避免测试间的数据依赖
- 使用有意义的测试数据
- 考虑数据的业务合理性
### 5. 断言设计
- 验证关键的业务结果
- 检查页面跳转的正确性
- 验证错误提示的准确性
- 确认数据保存的成功性
## UI测试开发完整流程(必须按顺序执行)
### 🔴 步骤1:创建测试分支(强制要求,不可跳过)
**在开始任何代码编写之前,必须先执行分支操作:**
1. **检查当前分支状态**
```bash
git branch
git status
```
2. **创建并切换到测试分支**
```bash
git checkout -b uitest/{ModuleName}/{action}/{大模型名称}
```
3. **验证分支创建成功**
```bash
git branch # 确认带*的是新创建的测试分支
```
**⚠️ 关键要求:**
- 分支名必须包含模块名、功能、大模型标识
- 例如:`uitest/user/create/claude`
- 如果忘记创建分支,必须立即停止当前操作并创建分支
### 步骤2:分析待测功能
1. 确定测试的页面和功能模块
2. 分析用户操作流程和交互元素
3. 理解业务逻辑和验证规则
4. 识别可能的异常情况和边界条件
### 步骤3:设计测试场景
1. 设计正常业务流程测试用例
2. 设计表单验证和错误处理用例
3. 设计用户交互和页面状态用例
4. 确定断言点和期望结果
### 步骤4:编写UI测试脚本
1. 创建UI测试执行脚本,包含完整的测试场景描述
2. 实现UI测试类中的测试方法
3. 创建页面对象文件定义页面元素
4. 编写全面的断言验证
### 步骤5:验证测试脚本
1. **使用ZenTao UI测试框架运行测试脚本**
2. 验证所有测试场景都能正确执行
3. 确认页面操作和断言都符合预期
4. 如果测试失败,根据结果修改测试脚本
5. 重复验证直到所有测试通过
### 🔴 步骤6:提交和推送代码(强制要求)
**测试验证通过后,必须按以下步骤提交代码:**
1. **添加UI测试文件到git(仅添加测试相关文件)**:
```bash
git add module/{module}/test/lib/{action}.ui.class.php
git add module/{module}/test/ui/{action}.php
git add module/{module}/test/ui/page/{action}.php
# 注意:不要添加规范文档文件到测试分支!规范文档应保留在开发分支上
```
2. **提交代码**
```bash
git commit -m "+ [misc] Add UI tests for {ModuleName} {action} functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>"
```
3. **推送到远程仓库**
```bash
git push
```
4. **切换回开发分支**
```bash
git checkout {原开发分支名} # 如 release/21.7.5 或 master
```
**⚠️ 提交信息规范:**
- 必须以符号开头:`+`(新增)、`-`(删除)、`*`(修改)
- 格式:`{symbol} [misc] {描述}`
- 必须包含Claude Code标识
- 必须包含Co-Authored-By信息
## 环境配置要求
### 1. WebDriver配置
- 浏览器驱动版本匹配
- 浏览器启动参数配置
- 超时时间设置合理
- 窗口大小适配测试需要
### 2. 测试环境
- 测试数据库独立
- 配置文件环境隔离
- 文件上传路径可写
- 网络访问正常
### 3. 依赖组件
- PHP WebDriver扩展
- 浏览器驱动程序
- 测试框架依赖库
- 图片处理扩展(如需截图)
## 注意事项
### 1. 浏览器兼容性
- 不同浏览器的行为差异
- 元素定位方式的兼容性
- JavaScript执行的差异
- 页面渲染时间差异
### 2. 性能考虑
- 避免不必要的等待时间
- 合理控制测试数据量
- 并发测试的资源竞争
- 浏览器资源占用
### 3. 稳定性保证
- 网络超时处理
- 页面加载异常处理
- 元素查找失败处理
- 浏览器崩溃恢复
## 📋 UI测试开发检查清单
在完成UI测试脚本开发后,请确认以下所有步骤都已正确执行:
### 🔴 强制检查项(必须100%完成)
- [ ] **已创建测试分支**:分支名格式为 `uitest/{模块名}/{功能}/{大模型名}`
- [ ] **当前在测试分支上工作**:通过 `git branch` 确认当前分支正确
- [ ] **测试脚本验证通过**:所有测试场景都能正确执行
- [ ] **已提交代码到本地**:使用正确格式的提交信息
- [ ] **已推送到远程仓库**:执行 `git push` 推送测试分支
- [ ] **已切换回开发分支**:完成后切换回原始开发分支
### 📝 代码质量检查项
- [ ] **测试场景完整**:包含正常流程、表单验证、异常情况测试
- [ ] **页面元素定位准确**:使用稳定的选择器定位页面元素
- [ ] **等待机制合理**:适当的等待时间和等待条件
- [ ] **断言验证全面**:验证关键业务结果和页面状态
- [ ] **页面对象模式**:正确使用页面对象组织代码结构
### 🌐 UI测试特有检查项
- [ ] **浏览器兼容性考虑**:考虑不同浏览器的行为差异
- [ ] **元素等待策略**:使用显式等待而非固定延时
- [ ] **错误处理机制**:处理页面加载失败和元素查找异常
- [ ] **测试数据独立**:每个测试用例独立准备和清理数据
### ⚠️ 常见错误避免
- [ ] **避免直接在开发分支开发**:必须在测试分支上进行开发
- [ ] **避免使用不稳定的选择器**:优先使用ID和稳定属性
- [ ] **避免硬编码等待时间**:使用条件等待替代固定延时
- [ ] **避免不提交代码**:测试通过后必须提交并推送代码
- [ ] **避免忘记切换回开发分支**:完成后必须切换回原分支
- [ ] **避免将规范文档提交到测试分支**:规范文档应该保留在开发分支供后续参照使用
---
**🎯 最终目标:通过遵循本指导文档,生成高质量、稳定可靠的ZenTao UI自动化测试脚本,有效提升系统界面功能的测试覆盖率和测试效率。**
**🚨 重要提醒:任何跳过分支管理步骤的行为都违反了ZenTao开发规范,必须严格按照流程执行!**