Python Playwright UI自动化测试与AI结合实战
📖 前言
在现代Web应用开发中,UI自动化测试是保证产品质量的重要环节。本文将介绍如何基于Playwright构建企业级UI自动化测试框架,并探讨如何将AI技术融入测试流程,实现更智能的测试体验。
🏗️ 一、项目架构概览
auto_ui/
├── config/ # 配置模块
│ ├── settings.py # 环境配置
│ └── config.yaml # 配置文件
├── pages/ # 页面对象模块
│ ├── base_page.py # 基础页面类
│ ├── login_page.py # 登录页面
│ └── personal_workspace_page.py # 个人工作台页面
├── tests/ # 测试用例模块
│ ├── conftest.py # pytest配置
│ └── test_*.py # 测试用例
├── utils/ # 工具模块
│ ├── logger.py # 日志工具
│ └── decorators.py # 装饰器
└── logs/ # 日志目录
核心设计理念
-
Page Object模式 - 页面元素与业务逻辑分离
-
YAML配置管理 - 环境配置与代码解耦
-
统一日志系统 - 测试执行全程可追溯
-
AI智能辅助 - 智能元素定位与异常分析
🔧 二、核心模块实现
2.1 配置管理
# config/settings.py
from pathlib import Path
import yaml
CONFIG_PATH = Path(__file__).parent / "config.yaml"
def load_config():
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
_config = load_config()
BASE_URL = _config.get("base_url", "https://test.example.com")
TIMEOUT = _config.get("timeout", 30000)
HEADLESS = _config.get("headless", False)
BROWSER = _config.get("browser", "chromium")
WAIT_TIMEOUTS = {
"short": 2000,
"medium": 3000,
"long": 5000,
"page_load": 30000,
}
# config/config.yaml
base_url: "https://test.example.com"
timeout: 30000
headless: false
browser: "chromium"
use_system_chrome: false
2.2 基础页面对象
# pages/base_page.py
from playwright.sync_api import Page
from config.settings import WAIT_TIMEOUTS
class BasePage:
"""页面对象基类"""
def __init__(self, page: Page):
self.page = page
def wait_for_timeout(self, milliseconds: int):
"""等待指定毫秒"""
self.page.wait_for_timeout(milliseconds)
def wait_for_page_load(self, timeout: int = 30000):
"""等待页面加载完成"""
try:
self.page.wait_for_load_state("domcontentloaded", timeout=timeout)
self.page.wait_for_load_state("networkidle", timeout=timeout)
except Exception:
pass
def find_elements(self, selector: str):
"""查找多个元素"""
return self.page.query_selector_all(selector)
def find_element_contains_text(self, text: str):
"""查找包含指定文本的元素"""
return self.page.query_selector_all(f"text={text}")
def _wait_and_click_element(self, text: str, exact: bool = False, timeout: int = 5000):
"""等待并点击元素"""
try:
if exact:
locator = self.page.get_by_text(text, exact=True)
else:
locator = self.page.get_by_text(text)
locator.click(timeout=timeout)
return True
except Exception:
return False
2.3 智能元素定位(AI辅助)
传统UI自动化测试最头疼的问题就是元素定位不稳定。元素选择器经常因为前端变化而失效,导致测试频繁中断。通过智能文本提取和多重定位策略,我们可以大大提升元素定位的稳定性:
def get_all_dashboards(self) -> list:
"""智能获取下拉列表中的看板选项
这个方法展示了如何处理复杂的DOM结构:
1. 尝试获取子元素的纯文本
2. 过滤掉功能按钮(如"新建")
3. 去重避免重复
"""
dashboards = []
seen_names = set()
dropdown_selectors = [
".el-select-dropdown__item:visible",
".el-select-dropdown__list li:visible",
]
for selector in dropdown_selectors:
try:
locator = self.page.locator(selector)
if locator.count() > 0:
for i in range(locator.count()):
item = locator.nth(i)
if not item.is_visible():
continue
# 🔍 智能文本提取
text = self._extract_clean_text(item)
if not text or len(text) > 50:
continue
# 过滤功能按钮
if any(keyword in text for keyword in ["重置", "导出", "新建", "编辑"]):
continue
if text in seen_names:
continue
seen_names.add(text)
dashboards.append({
"name": text,
"element": item,
})
except Exception:
continue
return dashboards
def _extract_clean_text(self, element) -> str:
"""从复杂DOM元素中提取纯净文本
解决思路:
有些下拉选项会隐藏JSON数据在元素内
我们需要只提取可见的文本部分
"""
text = element.text_content() or ""
text = text.strip()
# 尝试获取子元素的纯文本
try:
span_elem = element.locator("span").first
if span_elem.count() > 0:
text = span_elem.text_content() or ""
text = text.strip()
except Exception:
pass
# 如果文本包含JSON,只取之前的部分
text = text.split("{")[0].strip()
return text
🤖 三、AI能力集成
3.1 智能异常检测
class BasePage:
"""包含AI异常检测的基础页面类"""
def __init__(self, page: Page):
self.page = page
self.error_dialog_selectors = [
".el-message-box__wrapper", # Element Plus错误弹框
".ant-modal-error-desc", # Ant Design错误弹框
"[class*='message-box']", # 自定义错误弹框
".el-message--error", # 错误提示
]
def check_error_dialog(self) -> tuple[bool, list]:
"""AI驱动的异常检测
Returns:
(has_error, error_messages): 是否存在错误及错误信息列表
"""
error_messages = []
for selector in self.error_dialog_selectors:
try:
locator = self.page.locator(selector)
if locator.count() > 0:
for i in range(locator.count()):
elem = locator.nth(i)
if elem.is_visible():
text = elem.text_content() or ""
error_messages.append(f"[错误弹框] {text[:200]}")
except Exception:
continue
return len(error_messages) > 0, error_messages
3.2 网络请求智能监听
def setup_network_failure_listener(self):
"""设置网络请求监听器
AI能力:
- 实时监控所有HTTP请求
- 自动识别失败请求(状态码>=400)
- 记录完整的请求信息用于分析
"""
failed_requests = []
all_requests = []
def handle_response(response):
status = response.status
all_requests.append({
"url": response.url,
"status": status,
"status_text": response.status_text,
})
# 🔍 智能识别失败请求
if status >= 400:
failed_requests.append({
"url": response.url,
"status": status,
"status_text": response.status_text,
})
self.page.on("response", handle_response)
return failed_requests, all_requests
📝 四、测试用例实现
4.1 个人工作台测试
# tests/test_personal_workspace.py
import pytest
from pages import LoginPage, PersonalWorkspacePage
from config.settings import TEST_USER
from utils import log_info, log_error, log_step
@pytest.mark.ui
class TestPersonalWorkspace:
@log_step("测试个人工作台页面加载和异常检测")
def test_personal_workspace_load_and_error_check(self, page: Page):
"""
测试个人工作台功能:
1. 登录系统
2. 遍历检查所有看板
3. 检测异常弹框
4. 监控网络请求
"""
login_page = LoginPage(page)
workspace_page = PersonalWorkspacePage(page)
# 步骤1: 登录
log_info("步骤1: 打开登录页面并登录系统")
login_page.navigate_to_login()
login_page.login(TEST_USER["username"], TEST_USER["password"])
assert login_page.is_login_successful(), "登录失败"
# 步骤2: 进入个人工作台
log_info("步骤2: 进入菜单个人事务-个人工作台")
assert workspace_page.navigate_to_personal_workspace(), "导航失败"
# 步骤3: 遍历检查所有看板
log_info("步骤3: 遍历检查所有看板")
iterate_result = workspace_page.iterate_all_dashboards_with_errors()
# 汇总结果
all_failed_requests = []
dashboards_with_errors = []
for dashboard in iterate_result["dashboards"]:
if dashboard["has_error"]:
dashboards_with_errors.append(dashboard)
all_failed_requests.extend(dashboard.get("failed_requests", []))
# 断言检查
if dashboards_with_errors:
pytest.fail(f"{len(dashboards_with_errors)} 个看板存在异常")
if all_failed_requests:
pytest.fail(f"页面存在 {len(all_failed_requests)} 个失败的请求")
# 输出最终报告
dashboard_names = [d['name'] for d in iterate_result['dashboards']]
log_info(f"[SUCCESS] 共检查 {iterate_result['total']} 个看板: {', '.join(dashboard_names)}")
4.2 看板智能遍历
def iterate_all_dashboards_with_errors(self) -> dict:
"""智能遍历所有看板并检测异常
AI优化:
1. 自动识别当前看板
2. 智能打开下拉列表
3. 过滤功能按钮
4. 逐个检查看板状态
"""
result = {"total": 0, "dashboards": []}
# 获取第一个看板
first_dashboard_name = self.get_current_dashboard_name()
# 设置监听器并检查
failed_requests_1, all_requests_1 = self.setup_network_failure_listener()
self.wait_for_timeout(WAIT_TIMEOUTS.get("short", 2000))
has_error_1, errors_1 = self.check_current_dashboard_for_errors()
result["dashboards"].append({
"name": first_dashboard_name,
"has_error": False,
"errors": errors_1,
"failed_requests": failed_requests_1,
"all_requests": all_requests_1,
})
# 打开下拉列表
self.click_dashboard_selector()
self.wait_for_timeout(WAIT_TIMEOUTS.get("short", 2000))
# 获取所有其他看板
dashboard_names = self._get_dashboard_names_from_dropdown(first_dashboard_name)
# 遍历检查每个看板
for idx, dashboard_name in enumerate(dashboard_names, start=2):
print(f"检查第 {idx} 个看板: {dashboard_name}")
# 设置新的监听器
failed_requests_n, all_requests_n = self.setup_network_failure_listener()
# 切换看板
self.switch_to_dashboard(dashboard_name)
self.wait_for_timeout(WAIT_TIMEOUTS.get("medium", 3000))
# 检查异常
has_error_n, errors_n = self.check_current_dashboard_for_errors()
result["dashboards"].append({
"name": dashboard_name,
"has_error": has_error_n or len(failed_requests_n) > 0,
"errors": errors_n,
"failed_requests": failed_requests_n,
"all_requests": all_requests_n,
})
result["total"] = len(result["dashboards"])
return result
💡 五、AI提示词实战
在实际项目中,我们可以利用AI的图像理解和自然语言处理能力,结合Playwright实现更智能的测试。以下是几个实用的提示词示例:
5.1 智能元素定位
当遇到复杂的下拉菜单时,传统CSS选择器可能不稳定。我们可以让AI帮助分析DOM结构:
提示词:
我需要定位一个下拉菜单中的选项,下拉选项包含JSON数据。
当前获取的文本是:'默认分组{"id":1193,"name":"默认分组"...}'
请帮我分析:
1. 如何只提取显示的文本"默认分组"而不是JSON
2. 是否有更稳定的CSS选择器可以定位到纯文本
3. 这种隐藏JSON在前端框架中常见吗?
技术栈:Element Plus + Vue
AI分析结果:
# 解决方案:使用子元素选择器
text = item.locator("span").first.text_content()
# 或使用文本分割
text = item.text_content().split("{")[0].strip()
5.2 异常根因分析
当测试失败时,可以让AI分析错误日志:
提示词:
测试日志显示:
- 页面加载失败
- 错误弹框内容:"网络请求超时"
- HTTP状态码:504
请分析:
1. 这可能是前端还是后端问题?
2. 需要查看哪些接口日志?
3. 如何在测试代码中添加针对性检测?
前端框架:Vue + Element Plus
后端:Spring Cloud
AI分析结果:
# 针对性检测504错误
if status == 504:
print("检测到网关超时,可能是后端服务重启")
# 截图保存现场
page.screenshot(f"504_error_{datetime.now()}.png")
5.3 智能遍历策略
对于动态数量的列表,我们可以让AI帮我们设计遍历策略:
提示词:
页面有一个看板下拉列表,数量不固定(1-10个不等)。
需要:
1. 遍历所有看板
2. 检查每个看板的数据加载是否正常
3. 过滤掉"新建看板"这个功能按钮
请设计:
1. 如何判断哪些是真正的看板哪些是按钮?
2. 如何处理列表为空的情况?
3. 如何避免重复遍历?
AI分析结果:
def get_all_dashboards(self) -> list:
"""智能获取看板列表"""
dashboards = []
seen_names = set() # 去重
for item in self.find_all_dropdown_items():
text = self.extract_clean_text(item)
# 过滤条件
if not text or len(text) > 50:
continue
if any(kw in text for kw in ["新建", "导出", "重置", "+"]):
continue # 跳过功能按钮
if text not in seen_names:
seen_names.add(text)
dashboards.append({"name": text, "element": item})
return dashboards
5.4 页面变化适应
当页面UI频繁变化时,可以用AI分析最佳适配策略:
提示词:
页面A从v1.0升级到v2.0后:
- 导航菜单从左侧移到了顶部
- 原来的".menu-item"选择器失效
- 文字从中文改成了英文
请分析:
1. 如何设计选择器让它更稳定(不易受UI变化影响)
2. 如何实现"Fallback"策略(主选择器失效时自动尝试备选)
3. 如何检测页面结构是否发生了重大变化?
AI分析结果:
def click_with_fallback(self, selectors: list) -> bool:
"""多重选择器Fallback"""
for selector in selectors:
try:
elem = self.page.locator(selector).first
if elem.is_visible():
elem.click()
return True
except Exception:
continue
# 最后的兜底方案:基于相对位置
return self.click_by_position()
5.5 测试用例生成
让AI帮助分析页面并生成测试用例骨架:
提示词:
请分析以下页面截图中的功能模块:
[图片]
页面说明:这是一个"个人工作台"页面,包含:
- 顶部导航栏
- 左侧菜单
- 中间是多个可切换的看板
- 每个看板有刷新、导出等功能按钮
请生成:
1. Page Object类的基本结构
2. 主要功能的方法名和参数
3. 可能的异常场景
4. 需要监控的API接口
预期用户角色:普通员工
AI生成的Page Object骨架:
class PersonalWorkspacePage(BasePage):
"""个人工作台页面"""
def __init__(self, page):
super().__init__(page)
self.menu_personal_affairs = "个人事务"
self.menu_personal_workspace = "个人工作台"
def navigate_to_personal_workspace(self) -> bool:
"""导航到个人工作台"""
pass
def switch_dashboard(self, dashboard_name: str) -> bool:
"""切换看板"""
pass
def refresh_current_dashboard(self) -> bool:
"""刷新当前看板"""
pass
def export_dashboard(self, format: str = "excel") -> bool:
"""导出看板数据
Args:
format: 导出格式 (excel/csv/pdf)
"""
pass
📊 六、测试执行与报告
运行命令
# 运行所有UI测试
pytest tests/ -v
# 运行特定测试类
pytest tests/test_personal_workspace.py -v
# 使用标记过滤
pytest -m ui -v
日志输出示例
2026-04-07 15:30:00 [INFO] 步骤1: 打开登录页面并登录系统
2026-04-07 15:30:05 [INFO] 步骤2: 进入菜单个人事务-个人工作台
2026-04-07 15:30:10 [INFO] 步骤3: 遍历检查所有看板
2026-04-07 15:30:12 [INFO] 第 1 个看板(默认): 个人工作台 - ✅ 正常
2026-04-07 15:30:15 [INFO] 检查第 2 个看板: 我的看板 - ✅ 正常
2026-04-07 15:30:18 [INFO] 检查第 3 个看板: 销售看板 - ✅ 正常
2026-04-07 15:30:20 [INFO] 步骤4: 检查页面请求接口
2026-04-07 15:30:20 [INFO] 请求总数: 150 个
2026-04-07 15:30:20 [INFO] 成功数量: 148 个
2026-04-07 15:30:20 [INFO] 失败数量: 2 个
2026-04-07 15:30:20 [ERROR] ❌ [404] https://api.example.com/xxx
2026-04-07 15:30:20 [ERROR] ❌ [500] https://api.example.com/yyy
2026-04-07 15:30:20 [SUCCESS] 共检查 3 个看板: 个人工作台, 我的看板, 销售看板
🎯 七、AI能力总结
|
能力 |
实现方式 |
价值 |
|---|---|---|
|
智能元素定位 |
多重选择器 + 文本提取 |
适应前端变化,减少维护成本 |
|
异常自动检测 |
弹框选择器监控 |
及时发现UI问题 |
|
请求智能监控 |
HTTP响应监听 |
发现API级别问题 |
|
智能遍历 |
自动识别看板列表 |
支持动态内容测试 |
|
日志智能分析 |
分类统计 + 失败聚合 |
快速定位问题根因 |
🚀 八、未来展望
-
视觉AI识别 - 利用CV技术识别页面元素变化
-
智能用例生成 - 基于页面结构自动生成测试用例
-
异常根因分析 - 结合日志和截图进行AI诊断
-
自愈能力 - 元素失效时自动寻找替代方案
📚 结语
通过将AI技术与传统UI自动化测试相结合,我们构建了一个更加智能、稳定的测试框架。核心思想是:让测试框架具备自我感知和适应能力,从而降低维护成本,提高测试效率。
希望本文对您有所帮助,欢迎交流讨论!
如果觉得有用,欢迎点赞、收藏、转发!
关注作者,获取更多测试开发干货~
更多推荐



所有评论(0)