免费POC, 零成本试错
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


初步调研|基于AI的自动化脚本自愈方案

发布日期:2025-09-02 17:21:50 浏览次数: 1531
作者:质量探索与实践

微信搜一搜,关注“质量探索与实践”

推荐语

AI助力UI自动化测试脚本自愈,智能解决元素定位失效等四大难题,显著提升脚本稳定性。

核心内容:
1. UI自动化测试脚本失败的四大主要原因分析
2. 基于Deepseek API的AI自愈方案设计与实现
3. 具体配置和脚本调整的实践指南

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家



背景

当前UI自动化测试脚本的稳定性受多种因素影响,导致执行失败率较高,主要失败原因包括:

1.元素定位失效:前端属性(ID/Class/XPath)动态变更,导致脚本无法识别元素

2.页面结构重构:DOM层级或组件库升级引发原有定位策略失效

3.环境问题:网络延迟、浏览器版本差异等造成执行中断

4.业务逻辑变更:需求调整导致流程变化,原有用例不再适用

所以引入AI辅助的自愈机制,希望通过 AI 实现智能定位修复,通过AI 识别页面结构变化,自动生成备选选择器,自动调整定位策略,以适应前端变更,从而减少人工干预,降低维护成本降低,提高脚本稳定性。




整体方案

根据UI自动化执行失败的原因,制定不同的修复策略,具体如下

本文主要分享通过调用AI api 根据错误原因,推荐心得元素定位器列表,逐一验证新的元素定位器,验证通过则修改脚本。



AI工具选择

DeepSeek API 开放平台地址:https://platform.deepseek.com/usage

调用方式:

import requests API_KEY = "your_api_key" url = "https://api.deepseek.com/v1/chat/completions" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": "deepseek-chat", "messages": [ {"role": "user", "content": "请解释Transformer的注意力机制"} ], "temperature": 0.7, "max_tokens": 1000 } response = requests.post(url, json=payload, headers=headers) print(response.json())

Deepseek API 返回数据类型:支持json数据返回




脚本调整

1. 配置文件 config.ini

配置文件内需要配置 deepseek的 api地址、api key信息,可见deepseek的api开放平台

AI_API_KEY = Bearer sk-keyxxxx #需要申请API_URL = https://api.deepseek.com/v1/chat/completions

2. 自愈脚本 self_healing_locator.py 

1. 构建promot模版

2. 调用AI API输出推荐选择器列表

3. 遍历校验候选选择器有效性,验证通过则修改脚本;

import reimport requestsfrom playwright.sync_api import Pagefrom common.rwConfig import rwConfigfrom common.log import logger# 获取配置文件信息AI_API_KEY = rwConfig().getConfigOption(option='AI_API_KEY', section="common")API_URL = rwConfig().getConfigOption(option='API_URL', section="common")class Healer:    def __init__(self, page: Page):        self.page = page    def deepseek(self, prompt: str) -> list:        """调用AI API 生成定位器"""        headers = {                    "Authorization": AI_API_KEY,                    "Content-Type""application/json"                }
        # 调用deepseek API        payload = {            "model""deepseek-chat",            "messages": [                {"role""user""content": prompt}            ],            "temperature"0.7,            "max_tokens"1000        }
        try:            response = requests.post(API_URL, headers=headers, json=payload)            logger.info(f"API调用成功: {response.json()['choices'][0]['message']['content']}")            selector_list = eval(response.json()['choices'][0]['message']['content'])            logger.info(f"AI推荐定位器列表: {selector_list}")            # 返回推荐列表            return selector_list
        except Exception as e:            logger.info(f"API调用失败: {str(e)}")            return []    def locator_prompt(self, pass_selector:str,failed_selector:str) ->str:        """构建元素定位提示词模板"""        # 获取正确元素区域的页面内容        dom_snippet =self.get_html_around_element(pass_selector)        # logger.info(f"截断dom内容: {dom_snippet}")        return f"""        请为Playwright测试生成3个可靠的元素定位器,要求:        1. 代替定位失败的选择器:{failed_selector}        2. 基于当前页面片段(可能被截断):{dom_snippet}
        输出content要求:        - 按稳定性优先级排序,剔除需要开发人员参与的定位方式        - 使用CSS或XPath格式        - 输出为Python列表格式,如:["1", "2"],剔除其他文案,仅保留list内容        """    def heal_element(self,pass_selector:str,failed_selector:str, test_file:str) ->bool:        """执行自愈流程"""        i = 0        prompt =self.locator_prompt(pass_selector,failed_selector)        logger.info(f"生成定位器提示词: {prompt}")        # 调用AI API 生成定位器        elements =self.deepseek(prompt)        # 验证选择器有效性        for selector in elements:            logger.info(f"第【{i+1}】次验证选择器有效性: {selector}")            if self._try_selector(selector):                # 更新测试脚本文件                logger.info(f"更新测试脚本文件: {test_file} 失败元素: {failed_selector} 新定位器: {selector}")                self.update_file(test_file, failed_selector, selector)                return True            else:                i += 1                  if i > 3:                    logger.info(f"AI推荐的选择器均 验证失败")                    return False
        logger.info(f"未找到有效定位器: {failed_selector}")        return False

    def get_html(self, selector, chars_before=2000, chars_after=10000):             # 获失败元素局部页面的HTML            full_html = self.page.content()            # 获取元素的outerHTML和位置            element_html = self.page.locator(selector).evaluate('el => el.outerHTML')            element_index = full_html.find(element_html)
            if element_index == -1:                logger.info("关键元素 未找到")                return None
            # 计算截取范围            start = max(0, element_index - chars_before)            end = min(len(full_html), element_index + len(element_html) + chars_after)            # 截取页面片段            surrounding_html = full_html[start:end]
            return surrounding_html    def try_selector(self, selector: str) -> bool:        """验证选择器有效性"""         try:            self.page.wait_for_selector(selector, timeout=3000)            logger.info(f"选择器 有效: {selector}")              return True        except:            logger.info(f"选择器 无效: {selector}")            return False    def update_file(self, file_path: str, old: str, new: str):        """更新测试脚本文件"""        with open(file_path, "r"as f:             content = f.read()
        updated = re.sub(            rf'([\'"])({re.escape(old)})([\'"])',            lambda m: f"{m.group(1)}{new}{m.group(3)}",            content        )        logger.info(f"更新后的内容: {updated}")        with open(file_path, "w"as f:            f.write(updated)

3. 测试demo

import pytestfrom playwright.sync_api import Page, expectfrom common.self_healing_locator import SaucedemoHealerfrom common.rwConfig import rwConfigfrom conftest import *URL = "https://xxxxxx/sso"headless=bool(int(rwConfig().getConfigOption(option='headless')))@pytest.fixture(scope="module")def page():    with sync_playwright() as playwright:        browser = playwright.chromium.launch(headless=headless)        #保存登录信息        # context = browser.new_context(storage_state="./data/state.json")        context = browser.new_context(        page=context.new_page()        page.goto(URL)        yield page        browser.close()
@pytest.fixturedef healer(page):    yield Healer(page)
def test_checkout_flow(page, healer: Healer):
    try:        page.goto(URL)        visible = page.get_by_role("heading", name="手机密码登录").is_visible()        if not visible:                # 切换到手机号密码登录页面                time.sleep(1)                page.locator("xxwc-togger-sign div").nth(1).click()        # 输入手机号        page.locator("#phone").fill("xxxxxx")        # 输入密码        page.locator("#pwd").fill("xxxxxx")        # 点击登录按钮        page.locator(".xxx-button-login").click()     except Exception as e:        if "#pwd" in str(e):            # 触发自愈流程:传入正确元素、失败元素             if healer.heal_element("#phone","#pwd", __file__):                pytest.fail("元素定位已自动修复,请重新运行测试")            else:                pytest.fail("元素定位修复失败 END")        raise e


当前阶段遗留问题:

1. 部分页面的html内容获取失败,导致推荐选择器获取失败

2.自愈脚本如何快速运用到全局case中

3. 在 pytest中,使用的是 --reruns或其他重试机制(如 pytest-rerunfailures),所以重试时不会自动加载修改后的脚本,因为:

  • Python 在首次导入模块时会将其编译为字节码(.pyc),并缓存在 __pycache__目录中。

  • 重试时pytest仍会使用内存中已加载的模块(即第一次执行时的脚本版本),不会重新读取磁盘上的修改


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询