AI 驱动 Selenium 测试框架最佳实践:从传统自动化到智能体测试

AI 驱动 Selenium 测试框架最佳实践:从传统自动化到智能体测试


一、引言:为什么传统 Selenium 框架正在被重新定义

在自动化测试领域摸爬滚打十年后,我越来越清晰地意识到:传统 Selenium 框架正站在一个转折点上

过去十年,我们习惯了这样的工作流:

  1. 手动审查页面 DOM
  2. 精心编写 CSS Selector 或 XPath
  3. 祈祷前端团队不要改 class 名
  4. 每次前端重构都花两天修定位器
  5. 维护一个越来越庞大的元素仓库,没人敢动

这不是自动化,这是维护负债的永续债

2024--2025 年,大语言模型(LLM)的能力跨越了一个临界点------AI 不再是仅仅能写代码的辅助工具,而是可以实时理解页面结构、推理元素位置、并在元素变化时自动修复定位器的智能引擎

我们需要一个 生产级 AI Selenium 测试框架,它不是一个概念验证,而是一个在真实项目中运行、支持 Claude / OpenAI / 本地模型三种 AI 提供商、具备自然语言驱动测试生成能力的完整框架。

本文将分享我在这个过程中的全部技术决策、架构模式和踩坑教训。


二、传统 Selenium 自动化的四大痛点

在进入 AI 方案之前,先对齐问题域。任何不谈痛点的方案都是耍流氓。

痛点 1:定位器的脆弱性

python 复制代码
# 传统写法 ------ 前端改了 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:自然语言需求到自动化脚本的翻译鸿沟

产品经理说:"在搜索框输入关键词,点搜索,验证结果页包含相关内容。"

测试人员要做的是:

  1. 理解这个描述
  2. 手动录制 / 编写操作步骤
  3. 手动定位页面元素
  4. 编写断言
  5. 处理等待和异常

五步里只有第一步是思维活动,剩下四步都是机械劳动。

痛点 4:页面元素变化时的"瞎子"模式

传统 Selenium 的 find_element 是一个刚性调用 :找到了就继续,找不到就抛出 NoSuchElementException 结束测试。它没有"换个方式找找"的能力,更没有"记住新位置,下次直接去"的智能。


三、AI 驱动的架构设计:核心原则

基于上述痛点,我在设计 AI Selenium 框架时确立了以下核心原则:

原则 1:渐进增强(Progressive Enhancement)

传统方式 100% 兼容,AI 作为能力增强层,而非替代层。

框架不做"推翻重来"的事。你之前写的所有 Selenium 代码------所有 find_element、所有 Page Object------都能继续跑。AI 在失败时介入,而不是在正常时抢道。

原则 2:语义优先,定位器次之

每个元素都应该有一个"人类能理解的语义描述",定位器只是实现细节。

python 复制代码
# 传统 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 在第一次访问时执行以下流程:
#mermaid-svg-PhExjB39tNUfSKkn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PhExjB39tNUfSKkn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PhExjB39tNUfSKkn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PhExjB39tNUfSKkn .error-icon{fill:#552222;}#mermaid-svg-PhExjB39tNUfSKkn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PhExjB39tNUfSKkn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PhExjB39tNUfSKkn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PhExjB39tNUfSKkn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PhExjB39tNUfSKkn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PhExjB39tNUfSKkn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PhExjB39tNUfSKkn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PhExjB39tNUfSKkn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PhExjB39tNUfSKkn .marker.cross{stroke:#333333;}#mermaid-svg-PhExjB39tNUfSKkn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PhExjB39tNUfSKkn p{margin:0;}#mermaid-svg-PhExjB39tNUfSKkn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PhExjB39tNUfSKkn .cluster-label text{fill:#333;}#mermaid-svg-PhExjB39tNUfSKkn .cluster-label span{color:#333;}#mermaid-svg-PhExjB39tNUfSKkn .cluster-label span p{background-color:transparent;}#mermaid-svg-PhExjB39tNUfSKkn .label text,#mermaid-svg-PhExjB39tNUfSKkn span{fill:#333;color:#333;}#mermaid-svg-PhExjB39tNUfSKkn .node rect,#mermaid-svg-PhExjB39tNUfSKkn .node circle,#mermaid-svg-PhExjB39tNUfSKkn .node ellipse,#mermaid-svg-PhExjB39tNUfSKkn .node polygon,#mermaid-svg-PhExjB39tNUfSKkn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PhExjB39tNUfSKkn .rough-node .label text,#mermaid-svg-PhExjB39tNUfSKkn .node .label text,#mermaid-svg-PhExjB39tNUfSKkn .image-shape .label,#mermaid-svg-PhExjB39tNUfSKkn .icon-shape .label{text-anchor:middle;}#mermaid-svg-PhExjB39tNUfSKkn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PhExjB39tNUfSKkn .rough-node .label,#mermaid-svg-PhExjB39tNUfSKkn .node .label,#mermaid-svg-PhExjB39tNUfSKkn .image-shape .label,#mermaid-svg-PhExjB39tNUfSKkn .icon-shape .label{text-align:center;}#mermaid-svg-PhExjB39tNUfSKkn .node.clickable{cursor:pointer;}#mermaid-svg-PhExjB39tNUfSKkn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PhExjB39tNUfSKkn .arrowheadPath{fill:#333333;}#mermaid-svg-PhExjB39tNUfSKkn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PhExjB39tNUfSKkn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PhExjB39tNUfSKkn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PhExjB39tNUfSKkn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PhExjB39tNUfSKkn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PhExjB39tNUfSKkn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PhExjB39tNUfSKkn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PhExjB39tNUfSKkn .cluster text{fill:#333;}#mermaid-svg-PhExjB39tNUfSKkn .cluster span{color:#333;}#mermaid-svg-PhExjB39tNUfSKkn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PhExjB39tNUfSKkn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PhExjB39tNUfSKkn rect.text{fill:none;stroke-width:0;}#mermaid-svg-PhExjB39tNUfSKkn .icon-shape,#mermaid-svg-PhExjB39tNUfSKkn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PhExjB39tNUfSKkn .icon-shape p,#mermaid-svg-PhExjB39tNUfSKkn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PhExjB39tNUfSKkn .icon-shape .label rect,#mermaid-svg-PhExjB39tNUfSKkn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PhExjB39tNUfSKkn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PhExjB39tNUfSKkn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PhExjB39tNUfSKkn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🤖 AI 定位流程






🖱️ 用户请求元素
是否有显式定位器

CSS / XPath / ID
尝试定位
成功
缓存 WebElement
返回
1️⃣ 获取当前页面 HTML

清理后
2️⃣ 构建 Prompt → LLM
3️⃣ LLM 返回定位结果
confidence ≥ 阈值

默认 0.85
缓存定位器
写入元素仓库

持久化
4️⃣ 使用新定位器

重试 find_element
记录到 healing_log

人工审核

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>、注释、空属性,保留 idclassdata-*nametypeplaceholderaria-*text() 等有用属性。这既节省 token 又提高准确率。
  • 要求 confidence 评分:让 AI 自己评估可靠程度,低于阈值时触发人工审核流程。
  • fallback_strategies:如果主策略失败,框架可以尝试备选方案,不需要再次调用 LLM。
  • reasoning 字段:记录 AI 的推理过程,方便调试和审查。

4.3 缓存策略:避免重复调用 LLM

LLM API 调用有三大问题:延迟高、成本高、有失败风险。我们设计了三级缓存:

缓存层级 作用域 失效条件 命中时延迟
内存缓存(dict) 当前测试会话 页面 URL 变化 ~0ms
元素仓库(YAML) 持久化跨会话 手动清理 ~5ms
会话内 WebElement 缓存 单个元素实例 元素被刷新 ~0ms
python 复制代码
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 抽象层,支持以下后端:

python 复制代码
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 实现全局透明自愈

python 复制代码
# 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  # 恢复原始方法

为什么这是最佳方案?

  1. 零侵入:被测代码不需要 import 任何自愈相关模块
  2. 全局生效 :所有 find_element 调用都自动获得自愈能力,包括第三方库
  3. 透明:测试用例完全不知道自愈发生了
  4. 可控 :通过 --ai-heal / --no-ai-heal CLI 标志在运行时可开关

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

json 复制代码
{
  "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 生成管线

pytest 文件系统 代码生成器 PO推断器 LLM解析器 pytest 文件系统 代码生成器 PO推断器 LLM解析器 #mermaid-svg-PI7xzbYhRfxNHol1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PI7xzbYhRfxNHol1 .error-icon{fill:#552222;}#mermaid-svg-PI7xzbYhRfxNHol1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PI7xzbYhRfxNHol1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PI7xzbYhRfxNHol1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PI7xzbYhRfxNHol1 .marker.cross{stroke:#333333;}#mermaid-svg-PI7xzbYhRfxNHol1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PI7xzbYhRfxNHol1 p{margin:0;}#mermaid-svg-PI7xzbYhRfxNHol1 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PI7xzbYhRfxNHol1 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PI7xzbYhRfxNHol1 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PI7xzbYhRfxNHol1 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PI7xzbYhRfxNHol1 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PI7xzbYhRfxNHol1 .sequenceNumber{fill:white;}#mermaid-svg-PI7xzbYhRfxNHol1 #sequencenumber{fill:#333;}#mermaid-svg-PI7xzbYhRfxNHol1 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PI7xzbYhRfxNHol1 .messageText{fill:#333;stroke:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PI7xzbYhRfxNHol1 .labelText,#mermaid-svg-PI7xzbYhRfxNHol1 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .loopText,#mermaid-svg-PI7xzbYhRfxNHol1 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PI7xzbYhRfxNHol1 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PI7xzbYhRfxNHol1 .noteText,#mermaid-svg-PI7xzbYhRfxNHol1 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-PI7xzbYhRfxNHol1 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PI7xzbYhRfxNHol1 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PI7xzbYhRfxNHol1 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PI7xzbYhRfxNHol1 .actorPopupMenu{position:absolute;}#mermaid-svg-PI7xzbYhRfxNHol1 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-PI7xzbYhRfxNHol1 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PI7xzbYhRfxNHol1 .actor-man circle,#mermaid-svg-PI7xzbYhRfxNHol1 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PI7xzbYhRfxNHol1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出 listTestStep {action, url/element, text} 从 URL 提取域名 → 推断 "BaiduPage" alt 自动运行(可选) 仅生成 用户 自然语言描述 listTestStep PO名称 + TestStep 生成 pages/ai_generated/baidu_page.py 组合测试用例 生成 tests/ai_generated/test_baidu.py 调用 pytest 测试结果 返回文件路径 用户

6.3 生成的代码品质

这部分的挑战不在于"能否生成",而在于生成可维护的代码。以下是一个真实生成示例(整理后):

python 复制代码
# 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
python 复制代码
# 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

生成的代码有两个关键设计

  1. AIElement 无显式定位器:所有元素依赖 AI 运行时定位。这意味着即使页面变化,生成的代码也不需要修改。
  2. 语义化的属性名 :直接使用中文(如 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

反检测配置示例(对爬虫类任务有用,测试环境也可避免反爬干扰):

python 复制代码
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

八、验证码处理策略(一个务实方案)

验证码是自动化测试的"最后一公里"。我们的方案采用了多策略降级

python 复制代码
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 的下一个演进方向是:

  1. 视觉定位层:当 DOM 定位器全部失败时,使用截图 + 视觉 AI 定位(如 Claude Vision),实现真正的"所见即所得"
  2. AI 驱动的断言:不硬编码预期结果,而是让 AI 理解业务规则并自动验证
  3. 失败根因分析:测试失败时,AI 自动分析日志 + 截图 + DOM,定位是元素变化还是业务逻辑问题
  4. 测试生成自优化:根据历史运行数据,AI 自动调整等待时间、定位策略、重试逻辑

这些方向我们在下一个版本已经开始探索------核心思路是让框架越来越不需要人工介入,但不是通过硬编码规则,而是通过 AI 的推理能力


十二、写在最后

构建这个 AI Selenium 框架的过程中,我最大的体会是:

AI 不是在"替代"测试工程师,而是在"解放"测试工程师

过去我们需要花费 60% 的时间在定位器维护和脚本调试上,只有 40% 的时间在做真正的测试设计。AI 框架的目标是把这个比例倒过来------让 AI 处理那些重复、脆弱、机械的工作,让人专注于不可替代的部分:测试策略、场景设计、质量风险评估


附:本文框架实践地址 ai-selenium-framework,一个生产级的 AI 驱动 Selenium 测试框架,支持 Claude / OpenAI / 本地模型多提供商,具备自愈定位、自然语言测试生成、自动截图报告等完整能力集,代码已开源。

相关推荐
人民新视野1 小时前
2026美墨加世界杯伊朗VS新西兰预测分析亚洋二线实力大比拼
人工智能
qq_411262421 小时前
四博智联AI开发宝典(2/3):后端部署、OTA与AT+MCP接入
人工智能·ai·四博
QiLinkOS1 小时前
极客精神与商业思维的融合实践(2)
c语言·c++·人工智能·算法·开源协议
逻辑君1 小时前
认知神经科学研究报告【20260071】
人工智能·深度学习·机器学习·数学建模
Eloudy1 小时前
伊辛解码(Ising Decoding)
人工智能·量子计算
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年6月12日
人工智能·python·ai·信息可视化·自然语言处理·ai编程·灵砚智能
deephub1 小时前
相关性与因果性:识别伪相关以提升模型在真实环境的可用性
人工智能·机器学习·数据挖掘·数据分析
2601_955505251 小时前
行业研究|AI-Ready高质量数据集建设难点与元数据标准化解决方案(基于国家数据局25号文)
人工智能·金融·能源·健康医疗·制造·政务
虾壳云官方1 小时前
【本地 AI 自动化最新工具】 OpenClaw 2.7.9 Windows 完整部署教程(包含安装包)
人工智能·windows·openclaw·openclaw安装·openclaw一键部署