基于 OpenClaw + Claude Code 的端到端研发自动化系统
版本:v1.0 | 编制日期:2026 年 3 月 14 日 | 适用阶段:UI 自动化测试验收
| 测试类型 | 描述 | 优先级 | 自动化程度 |
|---|---|---|---|
| 必选 功能测试 | 验证 UI 功能是否符合需求规格说明书 | P0 | 100% 自动化 |
| 必选 回归测试 | 确保新功能不影响现有功能 | P0 | 100% 自动化 |
| 可选 兼容性测试 | 跨浏览器、跨设备、跨分辨率测试 | P1 | 80% 自动化 |
| 可选 性能测试 | 页面加载时间、响应时间、资源消耗 | P1 | 70% 自动化 |
| 推荐 视觉回归测试 | 检测 UI 布局、样式、颜色的意外变化 | P2 | 90% 自动化 |
| 推荐 无障碍测试 | 验证 WCAG 2.1 AA 标准合规性 | P2 | 60% 自动化 |
| 字段名称 | 必填 | 数据类型 | 填写说明与示例 |
|---|---|---|---|
| 用例 ID | 必选 | String |
格式:UI_[模块]_[子模块]_[序号]示例: UI_LOGIN_AUTH_001, UI_ORDER_CREATE_015
|
| 用例名称 | 必选 | String |
格式:[操作]+[对象]+[预期结果] 示例:"用户使用正确凭据登录成功跳转首页" |
| 优先级 | 必选 | Enum |
P0-阻塞性关键路径 |
P1-核心功能 |
P2-重要功能 |
P3-边缘场景
|
| 所属模块 | 必选 | String | 示例:用户认证、订单管理、支付流程、商品搜索 |
| 前置条件 | 必选 | Array |
示例:["用户已注册", "网络连接正常", "测试数据已准备"]
|
| 测试步骤 | 必选 | Array |
格式:步骤编号 + 操作描述 + 元素定位器 示例:见下方详细示例 |
| 预期结果 | 必选 | Array |
要求:可量化、可验证的断言条件 示例:["页面 URL 包含'/home'", "显示欢迎消息"] |
| 测试数据 | 可选 | Object |
示例:{"username": "test_user", "password": "Test@123"}
|
| 元素定位器 | 必选 | Object |
格式:定位策略 + 定位值 示例:见下方定位器规范 |
| 超时设置 | 可选 | Integer |
单位:毫秒 默认:10000ms (10 秒) 示例: 30000
|
| 重试次数 | 可选 | Integer |
默认:0 推荐:对于不稳定测试设置为 2-3 |
| 截图策略 | 可选 | Enum |
on_failure-仅失败时 |
always-始终 |
never-从不
|
| 关联需求 ID | 推荐 | String |
示例:REQ-2026-0015, USER-STORY-123
|
| 自动化脚本路径 | 必选 | String |
格式:相对项目根目录的路径 示例: tests/ui/login/test_login_success.py
|
| 执行环境 | 可选 | Array |
示例:["Chrome-Latest", "Firefox-Latest", "Safari-17"]
|
| 备注 | 可选 | String | 特殊说明、已知问题、依赖项等 |
# 测试步骤 JSON 结构示例
{
"step_id": "STEP_001",
"action": "click",
"description": "点击登录按钮",
"locator": {
"strategy": "xpath",
"value": "//button[@id='login-btn']"
},
"wait_before": 1000,
"wait_after": 2000,
"expected_state": {
"element_visible": true,
"page_url_contains": "/dashboard"
}
}
| 操作类型 | 描述 | 参数示例 |
|---|---|---|
click |
点击元素 | locator |
input_text |
输入文本 | locator, text |
select_option |
选择下拉选项 | locator, option_value |
upload_file |
上传文件 | locator, file_path |
scroll_to |
滚动到元素 | locator |
hover |
鼠标悬停 | locator |
double_click |
双击元素 | locator |
right_click |
右键点击 | locator |
switch_frame |
切换 iframe | frame_locator |
switch_window |
切换窗口 | window_handle |
navigate_to |
导航到 URL | url |
refresh |
刷新页面 | - |
go_back |
浏览器后退 | - |
go_forward |
浏览器前进 | - |
execute_script |
执行 JavaScript | script, args |
| 定位策略 | 优先级 | 稳定性 | 示例 | 适用场景 |
|---|---|---|---|---|
data-testid |
P0 | ⭐⭐⭐⭐⭐ | data-testid="login-btn" |
所有自动化测试元素 |
id |
P1 | ⭐⭐⭐⭐⭐ | id="username" |
具有唯一 ID 的元素 |
name |
P2 | ⭐⭐⭐⭐ | name="email" |
表单输入字段 |
css_selector |
P2 | ⭐⭐⭐⭐ | .btn.primary |
具有特定类名的元素 |
xpath_relative |
P3 | ⭐⭐⭐ | //div[@class='card']//button |
相对位置查找 |
xpath_absolute |
避免 | ⭐ | /html/body/div[2]/... |
不推荐使用 |
link_text |
P2 | ⭐⭐⭐ | link_text="忘记密码" |
链接元素 |
partial_link_text |
P3 | ⭐⭐ | partial_link_text="忘记" |
部分文本匹配 |
tag_name |
P3 | ⭐⭐ | tag_name="input" |
按标签名查找 |
class_name |
P3 | ⭐⭐ | class_name="submit-btn" |
单一类名 |
| 成熟度等级 | 自动化率 | 覆盖率 | 稳定性 | 集成度 | 特征描述 |
|---|---|---|---|---|---|
| L1 初始级 | <20% | <30% | <70% | 手动触发 | 手工测试为主,少量自动化 |
| L2 可重复级 | 20-40% | 30-50% | 70-85% | 定时执行 | 核心流程自动化,定期执行 |
| L3 已定义级 | 40-60% | 50-70% | 85-95% | CI 触发 | 标准化流程,CI 集成 |
| L4 已管理级 | 60-80% | 70-90% | 95-98% | 流水线集成 | 量化管理,持续反馈 |
| L5 优化级 | >80% | >90% | >98% | 全自动 | AI 驱动,自愈能力,持续优化 |
project_root/
├── tests/
│ ├── ui/ # UI 自动化测试
│ │ ├── pages/ # Page Object 页面对象
│ │ │ ├── base_page.py
│ │ │ ├── login_page.py
│ │ │ └── dashboard_page.py
│ │ ├── components/ # 可复用组件
│ │ │ ├── header.py
│ │ │ └── navigation.py
│ │ ├── test_cases/ # 测试用例
│ │ │ ├── login/
│ │ │ ├── order/
│ │ │ └── payment/
│ │ ├── test_data/ # 测试数据
│ │ │ ├── users.json
│ │ │ └── products.csv
│ │ └── conftest.py # Pytest 配置
│ ├── api/ # API 测试
│ └── unit/ # 单元测试
├── frameworks/
│ ├── core/ # 核心框架
│ ├── utils/ # 工具函数
│ └── reporters/ # 报告生成器
├── config/
│ ├── environments/ # 环境配置
│ ├── browsers.json # 浏览器配置
│ └── test_settings.yaml # 测试设置
├── scripts/
│ ├── setup_env.sh # 环境初始化
│ ├── run_tests.sh # 执行脚本
│ └── generate_report.sh # 报告生成
├── reports/ # 测试报告输出
├── logs/ # 日志文件
├── screenshots/ # 截图存档
├── recordings/ # 录屏存档
├── requirements.txt # Python 依赖
├── package.json # Node.js 依赖
├── docker-compose.yml # Docker 配置
└── Jenkinsfile # CI/CD 配置
| 类型 | 命名规则 | 示例 |
|---|---|---|
| 测试类 | Test[FeatureName] |
TestLogin, TestOrderCreation |
| 测试方法 | test_[action]_[condition]_[result] |
test_login_with_valid_credentials_success |
| Page Object 类 | [PageName]Page |
LoginPage, DashboardPage |
| 定位器变量 | [ELEMENT]_[LOCATOR_TYPE] |
LOGIN_BUTTON_XPATH |
| 测试数据 | 小写 + 下划线 | valid_user, invalid_password |
| 数据类型 | 存储方式 | 更新频率 | 示例 |
|---|---|---|---|
| 静态数据 | JSON/YAML文件 | 低 | 配置参数、枚举值 |
| 动态数据 | 数据库/API生成 | 高 | 订单号、时间戳 |
| 敏感数据 | 加密存储/环境变量 | 中 | 密码、API Key |
| 大数据集 | CSV/数据库 | 中 | 商品列表、用户数据 |
// Jenkinsfile 示例
pipeline {
agent any
environment {
BROWSER = 'chrome'
ENVIRONMENT = 'staging'
REPORT_DIR = 'reports/ui-tests'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/org/repo.git'
}
}
stage('Setup Environment') {
steps {
sh 'docker-compose up -d selenium-grid'
sh 'pip install -r requirements.txt'
}
}
stage('Run UI Tests') {
parallel {
stage('Chrome Tests') {
steps {
sh 'pytest tests/ui/ --browser=chrome --alluredir=${REPORT_DIR}'
}
}
stage('Firefox Tests') {
steps {
sh 'pytest tests/ui/ --browser=firefox --alluredir=${REPORT_DIR}'
}
}
}
}
stage('Generate Report') {
steps {
sh 'allure generate ${REPORT_DIR} -o allure-report --clean'
allure results: '${REPORT_DIR}'
}
}
stage('Quality Gate') {
steps {
script {
def passedRate = sh(script: 'python scripts/calc_pass_rate.py', returnStdout: true).trim()
if (passedRate.toBigDecimal() < 97.0) {
error("测试通过率 ${passedRate}% 低于阈值 97%")
}
}
}
}
stage('Deploy to K8S') {
when {
branch 'main'
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
}
steps {
sh 'kubectl apply -f k8s/deployment.yaml'
sh 'kubectl rollout status deployment/app'
}
}
}
post {
always {
archiveArtifacts artifacts: '${REPORT_DIR}/**/*'
publishHTML(target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAllHistory: true,
reportDir: 'allure-report',
reportFiles: 'index.html',
reportName: 'UI Automation Test Report'
])
}
failure {
sh 'scripts/notify_failure.sh'
}
}
}
# docker-compose.yml - 测试环境
version: '3.8'
services:
selenium-hub:
image: selenium/hub:4.15
ports:
- "4442:4442"
- "4443:4443"
- "4444:4444"
chrome:
image: selenium/node-chrome:4.15
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
firefox:
image: selenium/node-firefox:4.15
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
test-runner:
build: .
depends_on:
- selenium-hub
environment:
- SELENIUM_HOST=selenium-hub
- BROWSER=chrome
volumes:
- ./reports:/app/reports
- ./screenshots:/app/screenshots
# k8s-deployment.yaml - KubeSphere 部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: ui-test-runner
namespace: testing
spec:
replicas: 5
selector:
matchLabels:
app: ui-test-runner
template:
metadata:
labels:
app: ui-test-runner
spec:
containers:
- name: test-runner
image: registry.example.com/ui-tests:latest
env:
- name: SELENIUM_HOST
value: "selenium-hub.testing.svc.cluster.local"
- name: PARALLEL_WORKERS
value: "5"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-ui-tests
namespace: testing
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点执行
jobTemplate:
spec:
template:
spec:
containers:
- name: test-runner
image: registry.example.com/ui-tests:latest
restartPolicy: OnFailure
| 监控指标 | 阈值 | 告警级别 | 通知方式 |
|---|---|---|---|
| 测试通过率 | <97% | 严重 | 邮件 + 钉钉 + 短信 |
| 执行时长 | >45 分钟 | 警告 | 钉钉 |
| Flaky 测试数 | >5 个 | 警告 | 邮件 |
| 环境可用性 | <99% | 严重 | 电话 + 短信 |
# test_login_success.py
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage
from utils.test_data import load_test_user
# 测试用例元数据
pytestmark = [
pytest.mark.ui,
pytest.mark.login,
pytest.mark.p0,
pytest.mark.test_id("UI_LOGIN_AUTH_001")
]
class TestLoginSuccess:
"""
测试用例:用户使用正确凭据登录成功
关联需求:REQ-2026-0015
优先级:P0
前置条件:
1. 用户已注册
2. 网络连接正常
3. 测试环境可用
"""
@pytest.fixture(autouse=True)
def setup(self, driver):
"""测试前置准备"""
self.driver = driver
self.login_page = LoginPage(driver)
self.dashboard_page = DashboardPage(driver)
self.user = load_test_user("valid_user")
def test_login_with_valid_credentials_success(self):
"""验证用户使用有效凭据登录成功跳转到仪表盘"""
# Step 1: 打开登录页面
self.login_page.navigate()
# Step 2: 输入用户名
self.login_page.enter_username(self.user["username"])
# Step 3: 输入密码
self.login_page.enter_password(self.user["password"])
# Step 4: 点击登录按钮
self.login_page.click_login_button()
# Step 5: 验证跳转到仪表盘页面
assert self.dashboard_page.is_displayed(), "未成功跳转到仪表盘"
# Step 6: 验证显示欢迎消息
welcome_message = self.dashboard_page.get_welcome_message()
assert self.user["username"] in welcome_message, \
f"欢迎消息不包含用户名:{welcome_message}"
# Step 7: 验证 URL 正确
assert "/dashboard" in self.driver.current_url, \
f"URL 不正确:{self.driver.current_url}"
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(self, item, call):
"""失败时自动截图"""
outcome = yield
report = outcome.get_result()
if report.when == 'call' and report.failed:
screenshot = self.driver.get_screenshot_as_png()
report.attach(screenshot, mime_type="image/png")
# pages/login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from .base_page import BasePage
class LoginPage(BasePage):
"""登录页面对象 - 封装所有登录相关操作"""
# 元素定位器 (使用 data-testid 优先)
USERNAME_INPUT = (By.CSS_SELECTOR, "[data-testid='username-input']")
PASSWORD_INPUT = (By.CSS_SELECTOR, "[data-testid='password-input']")
LOGIN_BUTTON = (By.CSS_SELECTOR, "[data-testid='login-button']")
ERROR_MESSAGE = (By.CSS_SELECTOR, "[data-testid='error-message']")
FORGOT_PASSWORD_LINK = (By.LINK_TEXT, "忘记密码?")
PAGE_URL = "/login"
PAGE_TITLE = "用户登录"
def __init__(self, driver, timeout=10):
super().__init__(driver, timeout=timeout)
self.url = self.base_url + self.PAGE_URL
def navigate(self):
"""导航到登录页面"""
self.driver.get(self.url)
self.wait_for_page_load()
return self
def enter_username(self, username):
"""输入用户名"""
self.find_element(*self.USERNAME_INPUT).send_keys(username)
return self
def enter_password(self, password):
"""输入密码"""
self.find_element(*self.PASSWORD_INPUT).send_keys(password)
return self
def click_login_button(self):
"""点击登录按钮"""
self.find_element(*self.LOGIN_BUTTON).click()
return self
def get_error_message(self):
"""获取错误提示信息"""
return self.find_element(*self.ERROR_MESSAGE).text
def is_error_displayed(self):
"""检查错误信息是否显示"""
return self.is_element_visible(*self.ERROR_MESSAGE)
def click_forgot_password(self):
"""点击忘记密码链接"""
self.find_element(*self.FORGOT_PASSWORD_LINK).click()
return self
def login(self, username, password):
"""封装完整登录流程"""
self.enter_username(username)
self.enter_password(password)
self.click_login_button()
return self
| 用例 ID | 用例名称 | 模块 | 优先级 | 前置条件 | 测试步骤 | 预期结果 | 测试数据 | 自动化脚本 | 执行状态 |
|---|---|---|---|---|---|---|---|---|---|
| UI_LOGIN_AUTH_001 | 用户使用正确凭据登录成功 | 用户认证 | P0 | 用户已注册 | 1.打开登录页 2.输入用户名 3.输入密码 4.点击登录 |
跳转首页,显示欢迎 | user:test001 pwd:Test@123 |
test_login_success.py | 通过 |
| UI_LOGIN_AUTH_002 | 用户使用错误密码登录失败 | 用户认证 | P0 | 用户已注册 | 1.打开登录页 2.输入用户名 3.输入错误密码 4.点击登录 |
显示错误提示 | user:test001 pwd:Wrong@123 |
test_login_fail.py | 通过 |
| UI_ORDER_CREATE_001 | 用户创建订单成功 | 订单管理 | P0 | 用户已登录 商品有库存 |
1.选择商品 2.点击购买 3.确认订单 4.支付 |
订单创建成功,显示订单号 | product:P001 qty:1 |
test_create_order.py | 通过 |
| 检查项 | 验收标准 | 实际结果 | 状态 | 备注 |
|---|---|---|---|---|
| P0 用例覆盖率 | 100% | 100% (50/50) | ✓ | - |
| P0 用例通过率 | 100% | 100% (50/50) | ✓ | - |
| 整体通过率 | ≥97% | 98.5% (197/200) | ✓ | 3 个 P3 用例失败 |
| 致命缺陷数 | 0 | 0 | ✓ | - |
| 严重缺陷数 | ≤2 | 1 | ✓ | 已提供规避方案 |
| 页面加载时间 (LCP) | ≤2.5 秒 | 1.8 秒 | ✓ | 平均值 |
| 测试 Flaky 率 | ≤2% | 1.2% | ✓ | 近 10 次构建 |
| 连续构建通过 | 3 次 | 5 次 | ✓ | - |
| 测试报告完整性 | 100% | 100% | ✓ | Allure 报告 |
| 代码审查通过率 | 100% | 100% | ✓ | 所有脚本已 review |