基于 OpenClaw + Claude Code 的端到端研发自动化系统
版本:v1.0 | 更新日期:2026 年 3 月 14 日
UI 自动化测试验收模块是"基于 OpenClaw + Claude Code 的端到端研发自动化系统"的关键环节,位于整个研发流程的最终验收阶段。本模块承接单元测试、集成测试之后的质量保障工作,通过 AI 驱动的智能化 UI 自动化测试,确保产品在前端交互层面的功能完整性、用户体验一致性和视觉呈现准确性。
在版本迭代后,自动执行既定的测试用例集,验证核心功能是否正常工作,确保新代码未引入回归缺陷。
同时在 Chrome、Firefox、Safari、Edge 等多浏览器上执行测试,确保 UI 在不同浏览器中表现一致。
自动切换不同屏幕尺寸和分辨率,验证页面在桌面端、平板、手机等设备上的适配效果。
模拟真实用户的完整操作流程,从登录到核心业务操作,验证端到端的用户体验流畅度。
在部署后立即执行关键路径测试,快速判断版本是否达到基本可用状态。
通过截图对比,检测 UI 元素的视觉变化,发现样式异常、布局错位等问题。
| 能力维度 | 具体描述 | 技术实现 |
|---|---|---|
| 智能用例生成 | 基于 PRD 文档、API 接口定义,自动生成 UI 测试用例 | Claude Code + OpenClaw Skills |
| 自然语言 scripting | 使用自然语言描述测试步骤,AI 自动转换为可执行脚本 | skill-creator 元技能 |
| 自适应元素定位 | 智能识别页面元素,即使 DOM 结构变化也能准确定位 | 多策略融合定位 + AI 视觉识别 |
| 动态等待机制 | 智能判断元素加载状态,避免硬编码等待时间 | 显式等待 + 条件轮询 |
| 智能断言验证 | 多维度验证测试结果,包括文本、属性、状态、视觉等 | 多层断言框架 |
| 自动错误恢复 | 检测到异常时自动重试或采取补救措施 | 异常处理策略库 |
| 详细报告生成 | 生成包含截图、日志、性能数据的 HTML 测试报告 | Allure + 自定义报告模板 |
| CI/CD无缝集成 | 与 Jenkins、GitLab CI、GitHub Actions 等工具集成 | Docker + K8S 容器化部署 |
UI 自动化测试验收模块采用分层架构设计,从上到下依次为:交互层、编排层、执行层、基础设施层。
| 层级 | 技术组件 | 版本要求 | 用途说明 |
|---|---|---|---|
| AI 引擎 | Claude Code | v2026.3+ | 代码生成、测试用例编写、脚本转换 |
| OpenClaw | v2026.3.7+ | Skill 管理、Agent 编排、任务执行 | |
| Skill Engine | 最新 | 声明式技能管理、动态工具注册 | |
| 自动化框架 | Selenium | 4.x+ | Web UI 自动化核心框架 |
| Playwright | 1.40+ | 现代化浏览器自动化(可选) | |
| Cypress | 13.x+ | 前端集成测试(可选) | |
| Appium | 2.x+ | 移动端 UI 自动化(可选) | |
| 执行环境 | Docker | 24.x+ | 容器化运行环境 |
| Kubernetes | 1.28+ | 容器编排与调度 | |
| KubeSphere | 3.4+ | 容器平台管理界面 | |
| CI/CD | Jenkins | 2.4xx+ | 持续集成服务器 |
| GitLab CI | 最新 | 代码仓库内置 CI(可选) | |
| GitHub Actions | 最新 | GitHub 工作流(可选) | |
| 报告工具 | Allure | 2.25+ | 测试报告生成与展示 |
| Custom HTML | - | 自定义报告模板 |
UI 自动化测试验收模块不是孤立存在的,它与端到端研发自动化系统中的其他模块紧密协作:
从需求管理系统(如 Jira、禅道)获取需求信息,作为测试用例生成的输入依据。
解析 PRD 文档中的功能描述、业务流程,提取测试点和验收标准。
获取前后端技术方案中的架构信息、技术选型,指导测试策略制定。
基于 API 接口定义(OpenAPI/Swagger),生成接口调用相关的 UI 测试场景。
接收 AI 生成的业务代码,触发对应的 UI 自动化测试进行验证。
在单元测试通过后,才启动 UI 自动化测试,形成测试金字塔。
集成测试验证服务间调用,UI 测试验证最终用户界面,互为补充。
部署完成后自动触发 UI 冒烟测试,验证部署成功与否。
# 推荐使用 Python 3.10+
python --version
pip --version
node --version
npm --version
docker --version
docker-compose --version
kubectl version --client
helm version
OpenClaw 是本系统的核心编排引擎,需要进行以下配置:
# 克隆 OpenClaw 仓库
git clone https://github.com/OpenClaw/openclaw.git
cd openclaw
# 安装依赖
pnpm install
# 环境变量配置
cp .env.example .env
# 编辑.env 文件,填入必要的配置
# skills/config.yaml
skills:
ui_automation:
enabled: true
browser_automation:
- selenium
- playwright
assertion_engine:
enabled: true
report_generator:
format: [html, json, xml]
ci_cd_integration:
enabled: true
jenkins:
url: ${JENKINS_URL}
credentials: ${JENKINS_CREDENTIALS}
kubernetes:
kubeconfig: ~/.kube/config
namespace: ui-testing
Claude Code 负责智能代码生成和测试脚本转换,需要正确配置 API 访问权限。
# 访问 Anthropic 开发者平台
# https://console.anthropic.com/
# 创建新的 API Key 并记录
# .env 文件
ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxx
CLAUDE_MODEL=claude-sonnet-4-20260101
CLAUDE_MAX_TOKENS=8192
# 使用 OpenClaw CLI 测试连接
openclaw ai test-connection
# 预期输出:Connection successful!
UI 自动化测试需要浏览器驱动程序来控制浏览器。
# Python 示例
pip install webdriver-manager
# 在测试代码中自动管理驱动
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
# docker-compose.yml
version: '3.8'
services:
chrome:
image: seleniarm/standalone-chromium:latest
ports:
- "4444:4444"
- "7900:7900"
environment:
- SE_NODE_BROWSER_VERSION=latest
- SE_NODE_GRID_URL=http://localhost:4444
shm_size: 2gb
firefox:
image: seleniarm/standalone-firefox:latest
ports:
- "4445:4444"
environment:
- SE_NODE_BROWSER_VERSION=latest
# selenium-grid-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: selenium-hub
spec:
replicas: 1
selector:
matchLabels:
app: selenium-hub
template:
metadata:
labels:
app: selenium-hub
spec:
containers:
- name: selenium-hub
image: selenium/hub:4.15.0
ports:
- containerPort: 4442
- containerPort: 4443
- containerPort: 4444
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: selenium-node-chrome
spec:
replicas: 3
selector:
matchLabels:
app: selenium-node-chrome
template:
metadata:
labels:
app: selenium-node-chrome
spec:
containers:
- name: selenium-node-chrome
image: selenium/node-chrome:4.15.0
env:
- name: SE_EVENT_BUS_HOST
value: "selenium-hub"
- name: SE_EVENT_BUS_PUBLISH_PORT
value: "4442"
- name: SE_EVENT_BUS_SUBSCRIBE_PORT
value: "4443"
基于 AI 的测试用例生成功能可以大幅减少手工编写测试用例的工作量。
# 使用 OpenClaw skill-creator 生成测试用例
openclaw skill create \
--type "ui_test_cases" \
--input "prd_document.md" \
--output "test_cases/" \
--model "claude-sonnet-4"
# 或使用自然语言描述
openclaw chat "为电商网站的登录页面生成 UI 测试用例,
包括正常登录、密码错误、账号锁定、记住我功能等场景"
# 基于 OpenAPI Specification 生成
openclaw skill api-to-ui-tests \
--spec "swagger.json" \
--output "generated_tests/" \
--framework "pytest"
# test_login.py
import pytest
from page_objects.login_page import LoginPage
from utils.test_data import TestData
class TestLogin:
"""登录功能测试套件"""
@pytest.fixture(autouse=True)
def setup(self, driver):
"""测试前准备"""
self.driver = driver
self.login_page = LoginPage(driver)
self.driver.get("https://example.com/login")
def test_login_success(self):
"""TC001: 正常登录成功"""
self.login_page.enter_username(TestData.VALID_USER)
self.login_page.enter_password(TestData.VALID_PASSWORD)
self.login_page.click_login_button()
# 断言:跳转到首页
assert "home" in self.driver.current_url
assert self.login_page.is_user_logged_in()
def test_login_invalid_password(self):
"""TC002: 密码错误提示"""
self.login_page.enter_username(TestData.VALID_USER)
self.login_page.enter_password(TestData.INVALID_PASSWORD)
self.login_page.click_login_button()
# 断言:显示错误消息
error_message = self.login_page.get_error_message()
assert "密码错误" in error_message
@pytest.mark.screenshot
def test_login_page_layout(self):
"""TC003: 登录页面布局验证"""
# 截图对比
self.login_page.take_screenshot("login_page_baseline.png")
assert self.login_page.compare_with_baseline()
准确的元素定位是 UI 自动化测试成功的关键。
# page_objects/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
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# 多策略定位:优先使用 data-testid,降级到其他策略
self.username_input_locators = [
(By.CSS_SELECTOR, "[data-testid='username']"),
(By.ID, "username"),
(By.NAME, "username"),
(By.XPATH, "//input[@placeholder='请输入用户名']"),
]
self.password_input_locators = [
(By.CSS_SELECTOR, "[data-testid='password']"),
(By.ID, "password"),
(By.NAME, "password"),
]
self.login_button_locators = [
(By.CSS_SELECTOR, "[data-testid='login-btn']"),
(By.XPATH, "//button[contains(text(), '登录')]"),
(By.CLASS_NAME, "btn-login"),
]
def _find_element(self, locators):
"""智能元素查找:尝试多个定位策略"""
for locator in locators:
try:
element = self.wait.until(
EC.presence_of_element_located(locator)
)
return element
except Exception:
continue
raise Exception("无法找到元素,尝试所有定位策略失败")
def enter_username(self, username):
input_field = self._find_element(self.username_input_locators)
input_field.clear()
input_field.send_keys(username)
def enter_password(self, password):
input_field = self._find_element(self.password_input_locators)
input_field.clear()
input_field.send_keys(password)
def click_login_button(self):
button = self._find_element(self.login_button_locators)
button.click()
# 使用 AI 视觉识别定位元素
def find_element_by_ai_vision(self, description):
"""
基于自然语言描述的 AI 视觉元素定位
例如:"蓝色的登录按钮"、"右上角的用户头像"
"""
from openclaw.skills.vision import VisionSkill
vision = VisionSkill()
screenshot = self.driver.get_screenshot_as_png()
# AI 分析截图并返回元素坐标
element_coords = vision.locate_element(
image=screenshot,
description=description
)
# 使用坐标点击元素
action = ActionChains(self.driver)
action.move_to_element_with_offset(
self.driver.find_element(By.TAG_NAME, "body"),
element_coords['x'],
element_coords['y']
).click().perform()
data-testid 属性,这是最稳定的定位方式测试脚本的执行可以通过多种模式进行。
# 单个测试文件
pytest tests/test_login.py -v
# 整个测试套件
pytest tests/ -v --tb=short
# 带覆盖率报告
pytest tests/ --cov=src --cov-report=html
# 并行执行加速
pytest tests/ -n auto
# 构建测试镜像
docker build -t ui-tests:latest .
# 运行测试容器
docker run --rm \
-v $(pwd)/tests:/app/tests \
-v $(pwd)/reports:/app/reports \
ui-tests:latest \
pytest tests/ --html=reports/index.html
# 创建测试任务 Job
kubectl apply -f test-job.yaml
# 查看执行状态
kubectl get jobs
kubectl logs job/ui-test-job
# test-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: ui-test-job
spec:
parallelism: 5 # 并行执行 5 个 Pod
completions: 10 # 总共完成 10 次
template:
spec:
containers:
- name: ui-tests
image: ui-tests:latest
command: ["pytest", "tests/", "--json-report"]
restartPolicy: OnFailure
# 使用 OpenClaw 命令行
openclaw skill execute \
--name "ui_automation" \
--params '{
"test_suite": "regression",
"browser": "chrome",
"environment": "staging"
}'
# 或通过钉钉机器人触发
# 发送消息:"@UI 测试机器人 执行回归测试"
断言是验证测试结果是否符合预期的关键步骤。
# 文本断言
assert "欢迎回来" in driver.page_source
# URL 断言
assert "/dashboard" in driver.current_url
# 元素存在性断言
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
element = wait.until(EC.presence_of_element_located((By.ID, "user-menu")))
assert element.is_displayed()
# 属性断言
submit_button = driver.find_element(By.CSS_SELECTOR, "[type='submit']")
assert submit_button.get_attribute("disabled") is None
# 数据库断言:验证数据持久化
import psycopg2
def verify_user_created(username):
conn = psycopg2.connect(
host="localhost",
database="test_db",
user="test_user",
password="test_pass"
)
cursor = conn.cursor()
cursor.execute(
"SELECT COUNT(*) FROM users WHERE username = %s",
(username,)
)
count = cursor.fetchone()[0]
assert count == 1, f"用户{username}未在数据库中创建"
conn.close()
# API 断言:验证后端状态
import requests
def verify_api_response():
response = requests.get("https://api.example.com/user/profile")
assert response.status_code == 200
data = response.json()
assert data['status'] == 'active'
# 截图对比断言
from PIL import Image
import numpy as np
def compare_screenshots(baseline, current, threshold=0.95):
"""
比较当前截图与基准截图的相似度
threshold: 相似度阈值,低于此值则断言失败
"""
baseline_img = Image.open(baseline)
current_img = Image.open(current)
# 调整尺寸一致
current_img = current_img.resize(baseline_img.size)
# 转换为 numpy 数组并计算差异
baseline_array = np.array(baseline_img)
current_array = np.array(current_img)
# 计算相似度
diff = np.abs(baseline_array.astype(float) - current_array.astype(float))
similarity = 1 - (np.sum(diff) / (diff.size * 255))
assert similarity >= threshold, \
f"视觉回归检测失败!相似度:{similarity:.2%} < {threshold:.2%}"
return similarity
详细的测试报告有助于快速定位问题和跟踪质量趋势。
# 安装 Allure
pip install allure-pytest
# 生成 Allure 数据
pytest tests/ --alluredir=./allure-results
# 启动 Allure 报告服务器
allure serve ./allure-results
# 或生成静态报告
allure generate ./allure-results -o ./allure-report --clean
allure open ./allure-report
# pytest-html
pip install pytest-html
pytest tests/ --html=reports/test_report.html --self-contained-html
# 带截图的 HTML 报告
pytest tests/ \
--html=reports/report.html \
--self-contained-html \
--screenshot-on-fail
# conftest.py
import pytest
from datetime import datetime
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""为测试报告添加额外信息"""
outcome = yield
report = outcome.get_result()
if report.when == 'call':
# 添加执行时间
report.duration = call.stop - call.start
# 添加环境信息
report.environment = {
'browser': 'Chrome 120.0',
'os': 'Ubuntu 22.04',
'resolution': '1920x1080',
'timestamp': datetime.now().isoformat()
}
# 失败时自动截图
if report.failed:
screenshot_path = f"screenshots/{item.name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
item.funcargs['driver'].save_screenshot(screenshot_path)
report.sections.append(('Screenshot', screenshot_path))
在人机协同模式下,人类测试工程师与 AI Agent 协作完成测试任务。
测试工程师通过 Web UI 或钉钉向 UI 测试数字人下发任务指令。
# 钉钉消息示例
@UI 测试助手 请对订单管理模块执行回归测试,
重点关注新增的批量导出功能
AI 解析任务描述,查询知识库和历史任务,生成执行计划。
# AI 返回的执行计划
【任务理解】
- 测试范围:订单管理模块
- 重点功能:批量导出功能
- 测试类型:回归测试
【执行计划】
1. 检查测试环境状态
2. 执行订单列表页面 UI 测试 (15 用例)
3. 执行订单详情页面 UI 测试 (12 用例)
4. 执行批量导出功能专项测试 (8 用例)
5. 生成测试报告并通知
预计执行时间:25 分钟
是否确认执行?[确认] [修改]
测试工程师确认或调整执行计划。
AI 按照计划自动执行测试用例,实时监控执行状态。
遇到异常时,AI 根据预设策略自动处理或请求人工介入。
# AI 通知
⚠️ 测试执行异常通知
测试用例:TC_Order_Export_003
异常信息:导出按钮未响应,超时 10s
已尝试操作:重新点击 (3 次)、刷新页面后重试
建议操作:
[1] 继续重试 (最多再试 2 次)
[2] 跳过此用例继续执行
[3] 暂停测试等待人工检查
[4] 标记为已知问题
测试完成后,AI 生成详细报告并发送给相关人员。
在全自动模式下,UI 测试数字人基于自主意识引擎,无需人工干预即可完成测试任务。
# 自主意识配置示例
# config/autonomous_triggers.yaml
autonomous_mode:
enabled: true
triggers:
- name: "版本更新触发"
type: "event_driven"
condition: |
ci_pipeline.status == "success" AND
deployment.environment == "staging"
action: "execute_smoke_test"
- name: "定时回归测试"
type: "schedule"
cron: "0 2 * * *" # 每天凌晨 2 点
action: "execute_regression_test"
- name: "质量波动检测"
type: "metric_based"
condition: |
error_rate.last_hour > 5% OR
response_time.p95 > 2000ms
action: "execute_targeted_test"
- name: "周期性巡检"
type: "interval"
interval: "30m"
action: "execute_health_check"
# 自主引擎工作流程
1. 感知层:监控系统事件、指标、日志
↓
2. 决策层:匹配触发条件,判断是否需要执行测试
↓
3. 规划层:选择测试套件、确定执行策略
↓
4. 执行层:调用 Skill 执行测试
↓
5. 反馈层:分析结果、生成报告、通知相关人员
将 UI 自动化测试集成到 CI/CD 流水线,实现质量门禁自动化。
// Jenkinsfile
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: ui-tests
image: ui-tests:latest
command: ['cat']
tty: true
'''
}
}
environment {
SELENIUM_GRID_URL = 'http://selenium-hub:4444/wd/hub'
TEST_ENVIRONMENT = 'staging'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install Dependencies') {
steps {
sh 'pip install -r requirements.txt'
}
}
stage('Run UI Tests') {
steps {
script {
// 执行 UI 自动化测试
sh '''
pytest tests/ui/ \\
--junitxml=reports/junit.xml \\
--html=reports/index.html \\
--alluredir=allure-results
'''
}
}
post {
always {
// 收集测试报告
junit 'reports/junit.xml'
archiveArtifacts artifacts: 'reports/**/*', allowEmptyArchive: true
allure results: [[path: 'allure-results']]
}
}
}
stage('Quality Gate') {
steps {
script {
// 质量门禁检查
def report = readJSON file: 'reports/test_summary.json'
def passRate = report.statistics.pass_rate
if (passRate < 95) {
error "测试通过率 ${passRate}% 低于阈值 95%,构建失败!"
}
}
}
}
stage('Deploy to Production') {
when {
branch 'main'
expression { env.BUILD_STATUS == 'SUCCESS' }
}
steps {
sh 'kubectl apply -f k8s/production/'
}
}
}
post {
always {
// 清理资源
sh 'kubectl delete jobs batch || true'
}
failure {
// 发送失败通知
slackSend channel: '#qa-alerts',
color: 'danger',
message: "UI 测试失败:${env.BUILD_URL}"
}
}
}
# .gitlab-ci.yml
stages:
- test
- deploy
variables:
SELENIUM_GRID_URL: "http://selenium-hub:4444/wd/hub"
PYTEST_ADDOPTS: "--junitxml=reports/junit.xml --html=reports/index.html"
ui_tests:
stage: test
image: ui-tests:latest
services:
- name: selenium/standalone-chrome:latest
alias: selenium
variables:
SELENIUM_HOST: selenium
script:
- pip install -r requirements.txt
- pytest tests/ui/ --cov=src
artifacts:
when: always
reports:
junit: reports/junit.xml
paths:
- reports/
- allure-results/
expire_in: 1 week
coverage: '/TOTAL.*?([0-9]{1,3}%)/'
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_COMMIT_BRANCH == "main"
deploy_production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl apply -f k8s/production/
environment:
name: production
url: https://example.com
only:
- main
needs:
- ui_tests
# .github/workflows/ui-tests.yml
name: UI Automation Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
ui-tests:
runs-on: ubuntu-latest
services:
selenium:
image: selenium/standalone-chrome:latest
ports:
- 4444:4444
options: --shm-size=2gb
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-html allure-pytest
- name: Run UI Tests
env:
SELENIUM_GRID_URL: http://localhost:4444/wd/hub
run: |
pytest tests/ui/ \\
--junitxml=reports/junit.xml \\
--html=reports/index.html \\
--alluredir=allure-results
- name: Upload Test Reports
uses: actions/upload-artifact@v4
if: always()
with:
name: test-reports
path: |
reports/
allure-results/
- name: Allure Report
uses: simple-elf/allure-report-action@master
if: always()
with:
allure_results: allure-results
gh_pages: gh-pages
allure_report: allure-report
- name: Quality Gate
run: |
python scripts/check_quality_gate.py \\
--threshold 95 \\
--junit-report reports/junit.xml
- name: Notify Slack
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "❌ UI 测试失败:${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
# 推荐格式:TC_模块_功能_场景_序号
test_login_success_with_valid_credentials.py # ✅ 好
test_login.py # ❌ 过于笼统
# 示例
TC_Auth_Login_001_正常登录.py
TC_Auth_Login_002_密码错误提示.py
TC_Auth_Login_003_账号锁定处理.py
TC_Order_Create_001_创建普通订单.py
TC_Order_Create_002_创建预售订单.py
# utils/test_data.py
from faker import Faker
from datetime import datetime, timedelta
fake = Faker('zh_CN')
class TestData:
"""测试数据工厂"""
@staticmethod
def valid_user():
return {
'username': fake.user_name(),
'email': fake.email(),
'password': 'Test@123456',
'phone': fake.phone_number()
}
@staticmethod
def invalid_password():
return 'WrongPassword123!'
@staticmethod
def locked_account():
return {
'username': 'locked_user',
'password': 'Test@123456',
'lock_reason': '多次登录失败'
}
@staticmethod
def order_data(order_type='normal'):
base_data = {
'product_id': fake.uuid4(),
'quantity': fake.random_int(1, 10),
'shipping_address': fake.address()
}
if order_type == 'presale':
base_data['presale_deposit'] = 100
base_data['presale_end_time'] = datetime.now() + timedelta(days=7)
return base_data
# ✅ 好的做法:每个测试用例独立
class TestOrder:
@pytest.fixture(autouse=True)
def setup(self, driver, create_test_data):
"""每个测试前准备独立数据"""
self.order = create_test_data.order()
def test_order_create(self):
# 不依赖其他测试的状态
pass
def test_order_cancel(self):
# 使用 fixture 创建新订单,不依赖 test_order_create
new_order = create_test_data.order()
pass
# ❌ 坏的做法:测试用例相互依赖
def test_step_1():
global order_id
order_id = create_order()
def test_step_2():
# 依赖 test_step_1 创建的 order_id
cancel_order(order_id) # 如果 test_step_1 失败,此测试也会失败
# 推荐的定位策略优先级(从高到低)
LOCATOR_PRIORITY = [
'data-testid', # 1. 测试专用属性(最稳定)
'id', # 2. 唯一 ID
'name', # 3. 表单元素 name
'css_selector', # 4. CSS 选择器
'xpath', # 5. XPath(最后选择)
]
# HTML 示例:为自动化测试添加 data-testid
# 测试代码
submit_btn = driver.find_element(By.CSS_SELECTOR, "[data-testid='submit-btn']")
# ❌ 避免使用绝对 XPath
driver.find_element(By.XPATH, "/html/body/div[2]/div[1]/button[3]")
# ❌ 避免使用易变的文本
driver.find_element(By.XPATH, "//button[contains(text(), '点击这里提交订单')]")
# ✅ 使用稳定的属性
driver.find_element(By.CSS_SELECTOR, "[data-testid='order-submit']")
# ✅ 使用相对 XPath
driver.find_element(By.XPATH, "//form[@id='order-form']//button[@type='submit']")
# 等待动态加载的内容
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
# 等待元素可见
element = wait.until(
EC.visibility_of_element_located((By.ID, "dynamic-content"))
)
# 等待元素可点击
button = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "[data-testid='load-more']"))
)
# 等待文本出现
wait.until(
EC.text_to_be_present_in_element((By.CLASS_NAME, "status"), "已完成")
)
# 等待 iframe 加载完成
wait.until(
EC.frame_to_be_available_and_switch_to_it((By.ID, "payment-frame"))
)
# 使用 pytest-xdist 并行执行
pip install pytest-xdist
# 自动检测 CPU 核心数
pytest tests/ -n auto
# 指定并行数量
pytest tests/ -n 8
# 按模块分组并行
pytest tests/ -n logical --dist loadgroup
# 使用标记筛选测试
@pytest.mark.smoke
def test_critical_path():
pass
@pytest.mark.regression
def test_full_flow():
pass
# 执行特定标记的测试
pytest tests/ -m smoke # 只执行冒烟测试
pytest tests/ -m "not slow" # 排除慢测试
# conftest.py
import pytest
from selenium import webdriver
@pytest.fixture(scope="session")
def browser_session():
"""会话级浏览器实例(所有测试共享)"""
driver = webdriver.Chrome()
yield driver
driver.quit()
@pytest.fixture(scope="module")
def driver(browser_session):
"""模块级浏览器实例(模块内共享)"""
yield browser_session
@pytest.fixture(scope="function")
def fresh_driver():
"""函数级浏览器实例(每个测试独立)"""
driver = webdriver.Chrome()
yield driver
driver.quit()
# 1. 更新浏览器驱动
webdriver-manager update
# 2. 重新安装浏览器
sudo apt-get install --reinstall google-chrome-stable
# 3. 安装依赖库 (Linux)
sudo apt-get install libnss3 libgconf-2-4 libxi6
# 1. 添加显式等待
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.presence_of_element_located((By.ID, "target"))
)
# 2. 切换到 iframe
driver.switch_to.frame("iframe-id")
element = driver.find_element(By.ID, "target")
# 3. 使用多策略定位
locators = [
(By.CSS_SELECTOR, "[data-testid='target']"),
(By.ID, "target"),
(By.XPATH, "//button[contains(text(), '目标')]")
]
for locator in locators:
try:
element = driver.find_element(*locator)
break
except NoSuchElementException:
continue
# 1. 启用并行执行
pytest tests/ -n auto
# 2. 使用 Headless 模式
options = webdriver.ChromeOptions()
options.add_argument('--headless')
# 3. 禁用不必要的资源加载
options.add_argument('--disable-gpu')
options.add_argument('--disable-images')
options.add_argument('--blink-settings=imagesEnabled=false')
# 4. 优化等待策略(避免硬编码 sleep)
# ❌ 坏做法
time.sleep(5)
# ✅ 好做法
wait.until(EC.element_to_be_clickable(element))
# 1. 使用重试机制
@pytest.mark.flaky(reruns=3, reruns_delay=2)
def test_unstable_feature():
pass
# 2. 确保测试数据隔离
@pytest.fixture
def clean_database():
# 每个测试前清理数据库
truncate_all_tables()
yield
# 3. 添加更精确的等待条件
wait.until(lambda driver:
driver.execute_script("return window.appReady === true")
)
| 错误代码 | 错误信息 | 可能原因 | 解决方案 |
|---|---|---|---|
E001 |
SessionNotCreatedException | 浏览器驱动与浏览器版本不兼容 | 更新浏览器和驱动到匹配版本 |
E002 |
TimeoutException | 元素加载超时 | 增加等待时间或检查网络状况 |
E003 |
ElementClickInterceptedException | 元素被其他元素遮挡 | 关闭弹窗或滚动到元素位置 |
E004 |
StaleElementReferenceException | 元素已从 DOM 中移除 | 重新定位元素 |
E005 |
NoSuchWindowException | 窗口已关闭或不存在 | 检查窗口句柄,切换到有效窗口 |
E006 |
UnexpectedAlertPresentException | 出现未处理的 alert 弹窗 | 添加 alert 处理逻辑 |
E007 |
InvalidSelectorException | CSS/XPath 选择器语法错误 | 检查并修正选择器语法 |
E008 |
MoveTargetOutOfBoundsException | 目标元素不在可视区域 | 先滚动到元素位置再操作 |
# Skill 名称:ui_automation
# 版本:1.0.0
## 接口:execute_test
描述:执行 UI 自动化测试套件
请求参数:
{
"test_suite": "string", # 测试套件名称 (required)
"environment": "string", # 测试环境 (optional, default: staging)
"browser": "string", # 浏览器类型 (optional, default: chrome)
"parallel": "integer", # 并行执行数 (optional, default: 1)
"timeout": "integer", # 超时时间 (秒) (optional, default: 300)
"notify": "boolean" # 是否发送通知 (optional, default: true)
}
响应:
{
"job_id": "string", # 任务 ID
"status": "string", # 初始状态:queued
"estimated_duration": "integer" # 预计执行时长 (秒)
}
## 接口:get_test_status
描述:查询测试执行状态
请求参数:
{
"job_id": "string" # 任务 ID (required)
}
响应:
{
"job_id": "string",
"status": "string", # queued/running/completed/failed
"progress": "integer", # 进度百分比 (0-100)
"passed": "integer", # 通过用例数
"failed": "integer", # 失败用例数
"report_url": "string" # 报告链接
}
## 接口:generate_test_cases
描述:基于 PRD 或 API 定义生成测试用例
请求参数:
{
"source_type": "string", # prd/api_spec/user_story
"source_content": "string", # 源内容 (文本或文件路径)
"output_format": "string" # pytest/unittest/cucumber
}
响应:
{
"test_cases": [
{
"id": "string",
"name": "string",
"description": "string",
"steps": ["array of strings"],
"expected_result": "string"
}
],
"generated_count": "integer"
}
[pytest]
# 测试文件搜索路径
testpaths = tests
# 测试文件命名模式
python_files = test_*.py
python_classes = Test*
python_functions = test_*
# 默认参数
addopts =
-v
--tb=short
--strict-markers
--html=reports/index.html
--self-contained-html
--alluredir=allure-results
# 标记定义
markers =
smoke: 冒烟测试
regression: 回归测试
slow: 慢速测试
api: API 测试
ui: UI 测试
# 日志配置
log_cli = true
log_cli_level = INFO
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S
# 超时配置 (秒)
timeout = 300
# 重试配置
reruns = 2
reruns_delay = 3
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import os
def pytest_configure(config):
"""配置自定义标记"""
config.addinivalue_line(
"markers", "smoke: marks tests as smoke tests"
)
config.addinivalue_line(
"markers", "regression: marks tests as regression tests"
)
@pytest.fixture(scope="session")
def browser_options():
"""浏览器配置"""
options = Options()
# CI 环境使用 headless 模式
if os.getenv('CI'):
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1920,1080')
# 禁用图片加载加速
prefs = {
'profile.default_content_setting_values': {
'images': 2
}
}
options.add_experimental_option('prefs', prefs)
return options
@pytest.fixture(scope="function")
def driver(browser_options):
"""浏览器驱动 fixture"""
driver = webdriver.Chrome(options=browser_options)
driver.maximize_window()
driver.set_page_load_timeout(30)
yield driver
# 测试后清理
driver.quit()
@pytest.fixture(scope="function")
def base_url():
"""测试环境基础 URL"""
env = os.getenv('TEST_ENV', 'staging')
urls = {
'dev': 'http://localhost:3000',
'staging': 'https://staging.example.com',
'production': 'https://www.example.com'
}
return urls.get(env, urls['staging'])
FROM python:3.11-slim
# 安装系统依赖
RUN apt-get update && apt-get install -y \
wget \
gnupg \
unzip \
xvfb \
libgtk-3-0 \
libnss3 \
libgconf-2-4 \
&& rm -rf /var/lib/apt/lists/*
# 安装 Chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update \
&& apt-get install -y google-chrome-stable \
&& rm -rf /var/lib/apt/lists/*
# 设置工作目录
WORKDIR /app
# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制测试代码
COPY . .
# 设置环境变量
ENV SELENIUM_GRID_URL=http://selenium-hub:4444/wd/hub
ENV DISPLAY=:99
ENV TEST_ENV=staging
# 启动脚本
ENTRYPOINT ["sh", "-c", "Xvfb :99 -screen 0 1920x1080x24 & pytest tests/ --html=reports/index.html"]
| 术语 | 英文 | 解释 |
|---|---|---|
| UI 自动化测试 | UI Automation Testing | 使用自动化工具模拟用户操作,验证软件界面功能和用户体验的测试方法。 |
| Page Object 模式 | Page Object Pattern | 一种设计模式,将页面结构和操作封装成对象,提高测试代码的可维护性和复用性。 |
| 冒烟测试 | Smoke Testing | 验证系统核心功能是否正常的基础测试,通常在部署后立即执行。 |
| 回归测试 | Regression Testing | 在代码变更后重新执行之前的测试,确保新功能未破坏现有功能。 |
| 端到端测试 | E2E Testing | 模拟真实用户场景,从开始到结束的完整流程测试。 |
| 视觉回归测试 | Visual Regression Testing | 通过截图对比检测 UI 视觉变化的测试方法。 |
| Headless 浏览器 | Headless Browser | 无图形界面的浏览器,用于服务器环境执行自动化测试。 |
| WebDriver | WebDriver | W3C 标准的浏览器自动化协议,允许程序控制浏览器行为。 |
| Selenium Grid | Selenium Grid | 分布式测试执行架构,支持在多机器、多浏览器上并行执行测试。 |
| CI/CD | Continuous Integration/Continuous Deployment | 持续集成和持续部署,自动化软件开发和发布流程的实践。 |
| 质量门禁 | Quality Gate | 在 CI/CD 流程中设置的检查点,只有满足质量标准才能进入下一阶段。 |
| Flaky Test | Flaky Test | 不稳定的测试,有时通过有时失败,通常由竞态条件或外部依赖引起。 |
| Skill Engine | Skill Engine | OpenClaw 的技能管理框架,支持声明式技能定义和动态编排。 |
| Agent | Intelligent Agent | 具备自主感知、决策和执行能力的智能体,在本系统中指各类测试执行 Agent。 |