做好软件测试你要学习这些 AI 驱动 Selenium 测试框架最佳实践:从传统自动化到智能体测试
AI 驱动 Selenium 测试框架最佳实践:从传统自动化到智能体测试

一、引言:为什么传统 Selenium 框架正在被重新定义
在自动化测试领域摸爬滚打十年后,我越来越清晰地意识到:传统 Selenium 框架正站在一个转折点上。
过去十年,我们习惯了这样的工作流:
- 手动审查页面 DOM
- 精心编写 CSS Selector 或 XPath
- 祈祷前端团队不要改 class 名
- 每次前端重构都花两天修定位器
- 维护一个越来越庞大的元素仓库,没人敢动
这不是自动化,这是维护负债的永续债。
2024–2025 年,大语言模型(LLM)的能力跨越了一个临界点——AI 不再是仅仅能写代码的辅助工具,而是可以实时理解页面结构、推理元素位置、并在元素变化时自动修复定位器的智能引擎。
我们需要一个 生产级 AI Selenium 测试框架,它不是一个概念验证,而是一个在真实项目中运行、支持 Claude / OpenAI / 本地模型三种 AI 提供商、具备自然语言驱动测试生成能力的完整框架。
本文将分享我在这个过程中的全部技术决策、架构模式和踩坑教训。
二、传统 Selenium 自动化的四大痛点
在进入 AI 方案之前,先对齐问题域。任何不谈痛点的方案都是耍流氓。
痛点 1:定位器的脆弱性
# 传统写法 —— 前端改了 card-body__wrapper,你就死了
driver.find_element(By.CSS_SELECTOR, ".card-body__wrapper .btn-primary")
前端框架的普及(React / Vue / Angular)带来了动态 class 名、组件化 DOM 结构,让传统定位器频繁失效。CSS class 的语义从"描述元素是什么"退化为"描述元素长什么样",一夜之间就能变。
痛点 2:维护成本随项目规模超线性增长
一个 200 个测试用例的项目,如果页面元素变更 20%:
- 每个用例平均修复时间:15 分钟
- 批量修复时间:200 × 15 = 3000 分钟 = 50 小时人天
- 且修复后的定位器在下一次改版可能再次失效
痛点 3:自然语言需求到自动化脚本的翻译鸿沟
产品经理说:“在搜索框输入关键词,点搜索,验证结果页包含相关内容。”
测试人员要做的是:
- 理解这个描述
- 手动录制 / 编写操作步骤
- 手动定位页面元素
- 编写断言
- 处理等待和异常
五步里只有第一步是思维活动,剩下四步都是机械劳动。
痛点 4:页面元素变化时的"瞎子"模式
传统 Selenium 的 find_element 是一个刚性调用:找到了就继续,找不到就抛出 NoSuchElementException 结束测试。它没有"换个方式找找"的能力,更没有"记住新位置,下次直接去"的智能。
三、AI 驱动的架构设计:核心原则
基于上述痛点,我在设计 AI Selenium 框架时确立了以下核心原则:
原则 1:渐进增强(Progressive Enhancement)
传统方式 100% 兼容,AI 作为能力增强层,而非替代层。
框架不做"推翻重来"的事。你之前写的所有 Selenium 代码——所有 find_element、所有 Page Object——都能继续跑。AI 在失败时介入,而不是在正常时抢道。
原则 2:语义优先,定位器次之
每个元素都应该有一个"人类能理解的语义描述",定位器只是实现细节。
# 传统 Page Object
class LoginPage:
def __init__(self, driver):
self.login_btn = driver.find_element(By.ID, "login-btn")
# AI 增强的 Page Object
class LoginPage(BasePage):
@property
def login_btn(self):
return self.element("登录按钮", locator=("id", "login-btn"))
当 id="login-btn" 失效时,AI 根据"登录按钮"这个语义描述 + 当前页面 HTML 结构,实时推断出新的定位器。传统的 login_btn 属性变成了两阶段策略:优先用定位器,失败后用 AI。
原则 3:优雅降级(Graceful Degradation)
没有 AI API Key 时,框架应该仍然可用。
这是很多 AI 框架容易忽视的一点。我们实现了三层降级:
AI 在线模式 → AI 离线 Mock 模式 → 纯传统 Selenium 模式
每一层都向下兼容,确保开发环境、CI/CD 和企业内网环境都能运行。
原则 4:自愈能力应该是透明的
测试人员不需要知道元素被"修复"了——框架默默地做这件事,记录下来,下次更快。
四、AI 元素定位:框架的核心技术突破
4.1 两阶段定位器解析
这是框架最核心的创新能力。每个 AIElement 在第一次访问时执行以下流程:
4.2 Prompt 设计:如何让 AI 准确输出定位器
这里的关键不是"把 html 全扔给 AI",而是结构性表达。我们的 Prompt 模板核心结构如下:
你是一个 Selenium 元素定位专家。给定页面的 HTML 结构和元素描述,
返回该元素最可能的定位策略。
HTML 结构(已清理 script/style/注释):
{cleaned_html}
元素描述:{element_description}
请返回 JSON 格式:
{{
"locator_type": "css_selector | xpath | id | name | class_name | text",
"locator_value": "...",
"confidence": 0.0-1.0,
"reasoning": "简要推理过程",
"fallback_strategies": ["备选策略1", "备选策略2"]
}}
关键设计决策:
- 清理 HTML:移除
<script>、<style>、注释、空属性,保留id、class、data-*、name、type、placeholder、aria-*、text()等有用属性。这既节省 token 又提高准确率。 - 要求 confidence 评分:让 AI 自己评估可靠程度,低于阈值时触发人工审核流程。
- fallback_strategies:如果主策略失败,框架可以尝试备选方案,不需要再次调用 LLM。
- reasoning 字段:记录 AI 的推理过程,方便调试和审查。
4.3 缓存策略:避免重复调用 LLM
LLM API 调用有三大问题:延迟高、成本高、有失败风险。我们设计了三级缓存:
| 缓存层级 | 作用域 | 失效条件 | 命中时延迟 |
|---|---|---|---|
| 内存缓存(dict) | 当前测试会话 | 页面 URL 变化 | ~0ms |
| 元素仓库(YAML) | 持久化跨会话 | 手动清理 | ~5ms |
| 会话内 WebElement 缓存 | 单个元素实例 | 元素被刷新 | ~0ms |
def _ai_locate(self):
"""AI 定位——带三级缓存"""
cache_key = f"{self.description}|{self.driver.current_url}"
# 1. 内存缓存
if cache_key in self._locator_cache:
return self._locator_cache[cache_key]
# 2. 元素仓库缓存(YAML)
cached = self._repo_lookup(self.description)
if cached:
self._locator_cache[cache_key] = cached
return cached
# 3. LLM 调用
result = self._llm_locate_element()
self._locator_cache[cache_key] = result
self._repo_persist(self.description, result)
return result
实际效果:一个测试套件中,LLM 调用通常只在首次运行或页面改版后发生。日常运行几乎零 AI 开销。
4.4 多 AI 提供商抽象层
我们不绑定任何特定 AI 提供商。通过统一的 LLMClient 抽象层,支持以下后端:
class LLMClient:
async def ask(self, prompt: str) -> str:
if provider == "anthropic":
return await self._ask_claude(prompt)
elif provider == "openai":
return await self._ask_openai(prompt)
elif provider == "local":
return await self._ask_local(prompt)
else:
return self._mock_response(prompt) # 无 API Key 时降级
| 提供商 | 默认模型 | 推荐场景 |
|---|---|---|
| Claude (Anthropic) | claude-sonnet-4-20250514 | 元素定位准确率最高,推理能力强 |
| OpenAI | gpt-4o | 兼容性好,生态成熟 |
| 本地 (Ollama) | qwen2.5:7b | 内网部署,无数据离开风险 |
实测经验:在元素定位任务上,Claude Sonnet 的准确率最高,尤其是在需要理解"属性值含义"(如
aria-label="搜索"与button文本的语义匹配)时表现突出。
五、自愈引擎:让测试脚本自我修复
5.1 透明代理模式
这是整个框架中我最自豪的设计——通过 monkey-patch 实现全局透明自愈:
# conftest.py
@pytest.fixture(autouse=True)
def auto_heal(driver, heal_engine):
original_find = driver.find_element
def healing_find(by, value):
for attempt in range(3): # 最多重试 3 次
try:
return original_find(by, value)
except NoSuchElementException:
# 使用 AI 修复定位器
new_by, new_value = heal_engine.heal(by, value)
by, value = new_by, new_value
continue # 用新定位器重试
raise NoSuchElementException(f...)
driver.find_element = healing_find
yield
driver.find_element = original_find # 恢复原始方法
为什么这是最佳方案?
- 零侵入:被测代码不需要 import 任何自愈相关模块
- 全局生效:所有
find_element调用都自动获得自愈能力,包括第三方库 - 透明:测试用例完全不知道自愈发生了
- 可控:通过
--ai-heal/--no-ai-healCLI 标志在运行时可开关
5.2 自愈流程详解
driver.find_element(By.ID, "login-btn")
│
└── 抛出 NoSuchElementException
│
├── 1. HealEngine 在元素仓库中查找 "login-btn" 的语义描述
├── 2. 使用 AIElementLocator 根据语义描述重新定位
├── 3. AI 返回新定位器(confidence: 0.92)
├── 4. confidence ≥ 0.85 → 自动写回元素仓库
├── 5. 记录 healing_log.jsonl
├── 6. 使用新定位器重试 → 成功
└── 7. 测试继续执行,测试人员不需要知道发生了改变
5.3 置信度阈值策略
| 阈值区间 | 处理方式 |
|---|---|
| ≥ 0.95 | 自动批准 + 写入仓库 + 静默重试 |
| 0.85 – 0.94 | 自动批准 + 写入仓库 + 打 @pytest.mark.healed 标记 |
| 0.70 – 0.84 | 写入仓库标 pending_review + 记录日志 |
| < 0.70 | 仅记录日志 + 继续使用传统尝试 |
这个阈值体系是我在实践中反复调优得到的。太保守(> 0.95)会导致大量需要人工介入;太激进(< 0.80)会把错误定位器写进仓库,造成连锁失败。
5.4 Healing Log:可审计的自愈轨迹
每次自愈事件都会记录到 element_repository/healing_log.jsonl:
{
"timestamp": "2025-06-10T14:32:18.123Z",
"test": "test_login",
"element": "登录按钮",
"old_locator": {"type": "id", "value": "login-btn"},
"new_locator": {"type": "css_selector", "value": "button[data-testid='login-submit']"},
"confidence": 0.92,
"reasoning": "原login-btn未在页面中找到,发现button[data-testid='login-submit']具有相同的文本'登录'和功能语义",
"page_url": "https://example.com/login"
}
这个日志在项目中的实际价值:
- 当自愈发生异常时,可以回溯排查
- 为前端团队提供"你们的改动影响了哪些元素"的数据反馈
- 评估 AI 定位准确率的原始数据源
六、自然语言驱动的测试生成:从一句话到可执行测试
这是框架面向"让非技术人员也能写自动化测试"的尝试。用户只需要用自然语言描述操作步骤,框架自动生成 Page Object + 测试代码。
6.1 支持的 NLP 指令模式
框架内置了一个结构化自然语言解析器,支持以下指令模式:
| 你说的话 | 框架生成的动作 | 示例 |
|---|---|---|
| “打开 {url}” | driver.get(url) / page.open(url) |
“打开 https://baidu.com” |
| “在 {元素} 输入 {文本}” | 定位元素 → send_keys | “在搜索框输入 AI测试” |
| “点击 {元素}” | 定位元素 → click | “点击登录按钮” |
| “等待 N 秒” | time.sleep(N) |
“等待3秒” |
| “截图” | 截屏保存 | “截图保存” |
| “验证 {元素} 包含 {文本}” | 断言文本包含 | “验证结果包含成功” |
| “标题包含 {文本}” | 断言 title | “标题包含首页” |
| “滚动到 {元素}” | scrollIntoView | “滚动到底部” |
| “悬停到 {元素}” | ActionChains.move_to_element | “悬停到菜单” |
6.2 生成管线
6.3 生成的代码品质
这部分的挑战不在于"能否生成",而在于生成可维护的代码。以下是一个真实生成示例(整理后):
# pages/ai_generated/baidu_page.py
from pages.base_page import BasePage
class BaiduPage(BasePage):
url = "https://www.baidu.com"
@property
def 搜索框(self):
return self.element("百度首页搜索输入框")
@property
def 百度一下(self):
return self.element("百度一下搜索按钮")
def search(self, keyword: str):
self.open()
self.搜索框.input(keyword)
self.百度一下.click()
return self
# tests/ai_generated/test_baidu.py
import pytest
from pages.ai_generated.baidu_page import BaiduPage
@pytest.mark.ai_generated
class TestBaidu:
def test_baidu_search(self, driver):
page = BaiduPage(driver)
page.search("AI Selenium testing")
assert "AI" in page.title
生成的代码有两个关键设计:
- AIElement 无显式定位器:所有元素依赖 AI 运行时定位。这意味着即使页面变化,生成的代码也不需要修改。
- 语义化的属性名:直接使用中文(如
self.搜索框),降低理解门槛。
七、多浏览器驱动工厂与配置体系
7.1 DriverFactory 设计
DriverFactory.create_driver(browser="chrome", headless=True)
│
├── browser="chrome"
│ ├── webdriver-manager 自动下载 ChromeDriver
│ ├── 反检测配置(隐藏自动化标识)
│ └── 性能优化(禁用无用特性)
│
├── browser="firefox"
│ └── GeckoDriver + Firefox Options
│
├── browser="edge"
│ └── EdgeDriver + Edge Options
│
└── remote_url="http://grid:4444/wd/hub"
└── webdriver.Remote 连接到 Selenium Grid
反检测配置示例(对爬虫类任务有用,测试环境也可避免反爬干扰):
if browser == "chrome":
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
7.2 三层配置体系
| 层级 | 来源 | 配置项 | 优先级 |
|---|---|---|---|
| 1. CLI 参数 | --browser chrome --headless --ai-provider anthropic |
运行时切换 | 最高 |
| 2. 环境变量 + .env | ANTHROPIC_API_KEY, AI_PROVIDER |
API 密钥、环境配置 | 中 |
| 3. YAML 配置文件 | config/config.yaml |
默认参数、阈值、路径 | 低 |
推荐项目实践:
- YAML 配置提交到 git,作为团队默认值
.env不提交到 git(已在.gitignore中),每个人有自己的 API Key- CLI 参数用于 CI/CD 的特定配置(如
--headless)
八、验证码处理策略(一个务实方案)
验证码是自动化测试的"最后一公里"。我们的方案采用了多策略降级:
class CaptchaSolver:
def solve(self, image_path: str) -> str:
# 策略 1: 环境变量指定(CI 环境常用万能验证码)
if captcha_code := os.getenv("CAPTCHA_CODE"):
return captcha_code
# 策略 2: Tesseract OCR(简单验证码)
for preprocessor in [grayscale, threshold, denoise]:
result = ocr_with_preprocess(image_path, preprocessor)
if self._is_valid(result):
return result
# 策略 3: 人工介入(保存图片,等待用户输入)
return self._manual_input(image_path)
实际项目建议:
- 单元测试 / 集成测试:通过 API 绕过验证码(如
login_via_api()直接设置 token) - E2E 测试:使用测试环境的万能验证码
- OCR 方案:仅用于非核心流程的兜底
九、最佳实践总结(来自真实项目的经验)
9.1 架构层面
| 实践 | 详细说明 |
|---|---|
| Page Object 是基础,AI 是增强层 | AI 不能替代良好的架构设计,它让好架构变得更好 |
| AI 定位作为第二路径,不是唯一路径 | 有显式定位器时优先使用,能避免 95% 的 AI 调用 |
| 不做全量 AI 替代 | 只在异常时触发 AI,而非每个元素都走 AI 定位 |
| 一定要有 Mock 模式 | 没有 API Key 时框架必须可用,这是 CI 不可达环境的基础 |
9.2 Prompt 工程层面
| 实践 | 详细说明 |
|---|---|
| 发送前清洗 HTML | 移除 script/style/注释/空属性,可减少 60-80% 的 token 消耗 |
| 要求 confidence 评分 | 让 LLM 自评估,低分结果不自动写入仓库 |
| 结构化输出(JSON) | 比自由文本更易解析,再用 Pydantic/Schema 验证 |
| 提供 fallback 策略 | AI 返回时附带备选定位方案,减少二次调用 |
| 使用英文 Prompt | 实测英文 Prompt 的定位准确率高于中文 5-10% |
9.3 维护层面
| 实践 | 详细说明 |
|---|---|
| 元素仓库提交到 git | 追踪定位器变更历史,方便回滚错误修复 |
| Healing Log 定期审查 | 每周检查自愈日志,定位器频繁变化的页面需要通知前端团队 |
| AI 生成测试纳入 Code Review | AI 生成的测试代码需要人工审核后再加入回归套件 |
| 定期清理 Mock 数据 | 避免 Mock 模式下积累不可靠的测试数据 |
9.4 性能优化
| 实践 | 说明 |
|---|---|
| AI 定位结果会话内缓存 | 同一个测试会话内对同一元素的重复请求直接从内存返回 |
| 异步 LLM 调用 | 使用 asyncio 避免阻塞测试线程 |
| 控制单次发送的 HTML 大小 | 过大的页面只发送可见区域或关键容器 |
| CI 中使用 --no-ai-heal | 生产 CI 中 AI 修复是风险(因为无人工审核),只在开发/预发布环境开启自愈 |
十、框架选型建议:什么时候该用 AI Selenium?
适合 AI 增强的场景
| 场景 | 推荐度 | 理由 |
|---|---|---|
| 前端频繁改版(React/Vue 项目) | ⭐⭐⭐⭐⭐ | 自愈能力直接降低维护成本 |
| 多浏览器兼容测试 | ⭐⭐⭐⭐ | AI 理解各浏览器渲染差异,自动适配 |
| 快速原型验证 | ⭐⭐⭐⭐⭐ | 自然语言 → 测试脚本,分钟级产出 |
| 长流程 E2E 测试 | ⭐⭐⭐⭐ | 中间页面的变化不影响后续步骤 |
| 新项目初期测试建设 | ⭐⭐⭐⭐⭐ | 无需精确定位器,语义描述即可 |
不适合的场景
| 场景 | 理由 |
|---|---|
| 静态页面,从不变化 | 传统定位器足够,AI 是过度设计 |
| 高频率定时运行(如每分钟) | AI 调用的延迟和成本不值得 |
| 严格离线环境,无本地模型 | 无法运行 LLM |
| 页面极度复杂(数千元素) | HTML 太大导致 token 成本过高 |
十一、未来方向:从自愈到自主测试
基于当前框架的实践,我认为 AI Selenium 的下一个演进方向是:
- 视觉定位层:当 DOM 定位器全部失败时,使用截图 + 视觉 AI 定位(如 Claude Vision),实现真正的"所见即所得"
- AI 驱动的断言:不硬编码预期结果,而是让 AI 理解业务规则并自动验证
- 失败根因分析:测试失败时,AI 自动分析日志 + 截图 + DOM,定位是元素变化还是业务逻辑问题
- 测试生成自优化:根据历史运行数据,AI 自动调整等待时间、定位策略、重试逻辑
这些方向我们在下一个版本已经开始探索——核心思路是让框架越来越不需要人工介入,但不是通过硬编码规则,而是通过 AI 的推理能力。
十二、写在最后
构建这个 AI Selenium 框架的过程中,我最大的体会是:
AI 不是在"替代"测试工程师,而是在"解放"测试工程师。
过去我们需要花费 60% 的时间在定位器维护和脚本调试上,只有 40% 的时间在做真正的测试设计。AI 框架的目标是把这个比例倒过来——让 AI 处理那些重复、脆弱、机械的工作,让人专注于不可替代的部分:测试策略、场景设计、质量风险评估。
附:本文框架实践地址 ai-selenium-framework,一个生产级的 AI 驱动 Selenium 测试框架,支持 Claude / OpenAI / 本地模型多提供商,具备自愈定位、自然语言测试生成、自动截图报告等完整能力集,代码已开源。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)