1. 项目概述当截图“开口说话”自动化测试迎来新范式最近在折腾自动化测试时我一直在思考一个问题UI测试脚本的编写能不能再“懒”一点传统的流程要么是测试工程师对着UI设计稿或已上线的页面一行行手写定位元素和操作逻辑的代码要么是依赖录制回放工具但生成的脚本往往脆弱、难以维护一遇到UI微调就“全军覆没”。直到我开始尝试将多模态大模型MLLM引入这个流程一个全新的、高效的自动化脚本生成路径才变得清晰起来。今天要聊的这个实践就是围绕“Qwen3-VL-WEBUI”展开的核心目标很简单把一张产品UI截图“喂”给模型让它直接生成可执行、结构清晰的自动化测试脚本。这不仅仅是“用AI写代码”那么简单。它解决的是一个从视觉理解到逻辑转换的“最后一公里”问题。想象一下产品经理发来一张新功能的界面设计图或者你在回归测试中发现了一个偶现Bug并截了图。过去你需要人工解析图中的按钮、输入框、列表再转换成Selenium或Playwright的定位语句和操作命令。现在通过Qwen3-VL通义千问的多模态版本对图像内容的深度理解结合一个设计良好的WebUI交互界面我们可以让模型“看懂”截图并基于你的指令比如“为这个登录页面生成登录成功的测试用例”直接输出完整的测试脚本。这极大地降低了自动化测试的入门门槛提升了脚本创建的效率尤其适合快速迭代的敏捷开发环境和需要覆盖大量UI场景的测试需求。整个过程涉及几个关键角色Qwen3-VL作为“大脑”负责视觉识别与逻辑推理WEBUI作为“操作台”提供便捷的图像上传、指令输入和结果展示界面而最终产出的自动化测试脚本可能是基于Playwright、Selenium或Appium则是可直接投入使用的“武器”。接下来我将拆解从环境搭建、核心交互、到脚本优化落地的全流程并分享其中踩过的坑和提炼出的实战技巧。2. 核心工具链选型与部署实战工欲善其事必先利其器。要实现截图到脚本的自动化流水线我们需要搭建一个稳定、高效的工具环境。这里的选型直接决定了后续流程的顺畅度和产出脚本的质量。2.1 Qwen3-VL模型为什么是它以及如何部署在众多开源多模态大模型中我选择Qwen3-VL以Qwen2.5-VL为例主要基于以下几点考量强大的视觉问答VQA与细粒度识别能力它不仅能识别图片中的物体更能理解UI界面中的元素层级如导航栏、侧边栏、卡片、元素类型按钮、输入框、下拉菜单、表格和元素状态激活、禁用、选中。这对于准确生成操作指令至关重要。出色的代码生成与指令跟随能力作为通义千问家族成员其在代码生成任务上表现优异能够遵循“生成PythonPlaywright脚本”这类复杂指令并结构化输出。对中文UI和指令的友好支持在处理包含中文文本的界面截图时其识别准确率较高生成的脚本变量命名、注释也更符合中文开发者的习惯。灵活的部署选项支持通过Transformers库本地部署也提供了OpenAI兼容的API接口方便集成。本地部署方案推荐用于深度定制和隐私保护对于有一定GPU资源的团队本地部署能提供最好的可控性和数据安全性。以下是基于transformers和vLLM的部署步骤# 1. 创建并进入Python虚拟环境 python -m venv qwen_env source qwen_env/bin/activate # Linux/Mac # qwen_env\Scripts\activate # Windows # 2. 安装核心依赖 pip install transformers torch accelerate # 如果使用vLLM加速推理强烈推荐提升吞吐量 pip install vllm # 3. 下载模型以Qwen2.5-VL-7B-Instruct为例 # 可以从ModelScope或Hugging Face下载 from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline import torch model_id Qwen/Qwen2.5-VL-7B-Instruct tokenizer AutoTokenizer.from_pretrained(model_id, trust_remote_codeTrue) # 根据显卡显存选择加载方式 model AutoModelForCausalLM.from_pretrained( model_id, torch_dtypetorch.float16, # 半精度节省显存 device_mapauto, # 自动分配多GPU trust_remote_codeTrue ) # 使用vLLM部署以获得更高性能 from vllm import LLM, SamplingParams llm LLM(modelmodel_id, tensor_parallel_size1, gpu_memory_utilization0.9) # tensor_parallel_size为GPU数量注意Qwen3-VL系列模型对显存要求较高。7B版本至少需要16GB GPU显存才能流畅运行。如果资源有限可以考虑使用量化版本如GPTQ-Int4或使用云API方案。API服务化部署为了让WEBUI和其他服务方便调用我们需要将模型封装成API。使用FastAPI可以快速实现# server.py from fastapi import FastAPI, UploadFile, File, Form from PIL import Image import io from vllm import SamplingParams app FastAPI() # 初始化vLLM引擎 llm LLM(modelQwen/Qwen2.5-VL-7B-Instruct) app.post(/generate_script) async def generate_script( image: UploadFile File(...), instruction: str Form(请为这张UI截图生成Playwright自动化测试脚本), framework: str Form(playwright) ): # 读取并处理图片 image_data await image.read() img Image.open(io.BytesIO(image_data)) # 构建多模态提示词 # Qwen-VL的提示词需要特殊格式将图片作为消息的一部分 messages [ { role: user, content: [ {type: image, image: img}, # 传入PIL Image对象 {type: text, text: f{instruction}。请使用{framework}框架生成完整的Python测试脚本。} ] } ] # 将消息转换为模型接受的格式此处简化实际需按模型文档处理 prompt tokenizer.apply_chat_template(messages, tokenizeFalse) # 设置生成参数 sampling_params SamplingParams(temperature0.1, max_tokens2048, stop[/s]) # 推理生成 outputs llm.generate([prompt], sampling_params) generated_text outputs[0].outputs[0].text # 后处理提取代码块 import re code_pattern rpython\n(.*?)\n match re.search(code_pattern, generated_text, re.DOTALL) script_code match.group(1) if match else generated_text return {script: script_code, raw_response: generated_text}使用uvicorn server:app --host 0.0.0.0 --port 8000启动服务后我们就拥有了一个接收截图和指令返回测试脚本的API端点。2.2 WEBUI搭建Gradio vs. Streamlit的抉择有了模型API我们需要一个用户友好的界面。Gradio和Streamlit是两个热门选择我最终选择了Gradio原因如下对多模态输入的原生友好Gradio的Image组件处理图片上传和预览非常方便与模型的消息格式对接更顺畅。快速原型开发几行代码就能构建一个功能完整的交互界面迭代速度快。易于分享可以快速创建可公开访问的链接方便团队内部演示和测试。一个基础的Gradio WEBUI应用代码如下# webui_app.py import gradio as gr import requests import base64 from PIL import Image import io API_URL http://localhost:8000/generate_script # 指向刚才部署的API def generate_from_image(image, instruction, framework): if image is None: return 请先上传一张UI截图。, # 将Gradio的numpy数组或PIL Image转换为字节流 buffered io.BytesIO() image.save(buffered, formatPNG) img_bytes buffered.getvalue() # 调用后端API files {image: (ui_screenshot.png, img_bytes, image/png)} data {instruction: instruction, framework: framework} try: response requests.post(API_URL, filesfiles, datadata, timeout60) if response.status_code 200: result response.json() raw_output result.get(raw_response, ) script_code result.get(script, ) # 格式化显示 formatted_output f**生成的脚本**\npython\n{script_code}\n\n\n**模型原始回复**\n{raw_output} return formatted_output, script_code else: return fAPI调用失败状态码{response.status_code}, except Exception as e: return f请求发生错误{str(e)}, # 构建界面 with gr.Blocks(title截图转测试脚本生成器) as demo: gr.Markdown(# ️➡️ 截图转自动化测试脚本生成器) gr.Markdown(上传UI截图描述你的测试意图选择框架一键生成可运行的测试脚本。) with gr.Row(): with gr.Column(scale1): image_input gr.Image(label上传UI截图, typepil) instruction_input gr.Textbox( label测试指令, value请为这个登录页面生成一个测试脚本包含用户名密码输入和登录按钮点击。, lines3 ) framework_choice gr.Radio( choices[playwright, selenium, pytest需指定框架], valueplaywright, label测试框架 ) generate_btn gr.Button(生成脚本, variantprimary) with gr.Column(scale2): output_display gr.Markdown(label生成结果) code_output gr.Code(label纯净脚本代码, languagepython, interactiveTrue) # 绑定事件 generate_btn.click( fngenerate_from_image, inputs[image_input, instruction_input, framework_choice], outputs[output_display, code_output] ) # 添加示例 gr.Examples( examples[ [examples/login_page.png, 测试登录功能使用错误密码应提示‘密码错误’。, playwright], [examples/dashboard.png, 生成脚本验证顶部导航栏所有菜单项均可点击。, selenium], ], inputs[image_input, instruction_input, framework_choice], outputs[output_display, code_output], fngenerate_from_image, cache_examplesTrue ) if __name__ __main__: demo.launch(server_name0.0.0.0, server_port7860, shareFalse) # shareTrue可生成临时公网链接这个界面提供了图片上传、指令输入、框架选择、一键生成、结果展示含高亮代码和示例快速加载功能基本覆盖了核心操作流。2.3 测试脚本框架Playwright为何成为首选在生成的脚本框架选择上我优先推荐Playwright其次是Selenium。为什么是Playwright自动等待与稳定性Playwright内置了智能等待机制能自动等待元素可操作大大减少了因页面加载速度导致的“元素找不到”错误生成的脚本更健壮。强大的选择器引擎支持文本选择器、角色选择器等模型更容易生成准确且稳定的定位语句如page.get_by_role(button, name登录)。多语言、多浏览器支持一套脚本可在Chromium、Firefox、WebKit上运行且API设计现代简洁。录制功能可作为参考虽然我们不直接使用录制代码但Playwright Recorder生成的脚本结构清晰可以作为提示词的一部分教给模型让模型学习更地道的写法。因此在给模型的指令中我会明确要求“请使用Playwright框架生成一个完整的、可独立运行的Python测试脚本。脚本应包含必要的导入、浏览器启动、页面导航、元素操作使用get_by_role、get_by_text等稳定选择器、断言以及资源清理。”3. 从截图到可执行脚本的深度解析有了基础设施接下来我们深入核心环节模型是如何“看懂”截图并“写出”正确脚本的这个过程并非魔法其效果严重依赖于我们提供的“提示词工程”和后续的“脚本后处理”。3.1 提示词工程教会模型“如何思考”直接给模型一张图和一个简单指令它可能生成五花八门的代码。我们必须通过精心设计的提示词Prompt来约束和引导它。一个高效的提示词通常包含以下几个部分1. 角色定义与任务明确你是一名专业的自动化测试开发工程师。你的任务是根据用户提供的软件UI界面截图生成高质量、可直接执行的UI自动化测试脚本。2. 输出格式严格规定请严格按照以下格式输出 1. 脚本必须是一个完整的、可运行的Python文件。 2. 使用python ... 代码块包裹整个脚本。 3. 脚本顶部需有简要注释说明测试的功能点。 4. 使用Pytest或Unittest测试框架结构如使用def test_xxx():。 5. 对于UI元素定位优先使用Playwright的get_by_role(), get_by_text(), get_by_label()方法其次考虑get_by_test_id()。仅在必要时使用CSS或XPath。 6. 必须包含合理的等待如page.wait_for_selector和断言如expect()。 7. 脚本最后必须妥善关闭浏览器。3. 上下文信息补充关键这是提升生成准确性的核心。我们不能只给图还要告诉模型图中可能存在的“盲点”。额外注意 - 截图可能不完整或模糊请根据常见UI设计模式推断元素功能。例如一个右侧有眼睛图标的输入框通常是密码框。 - 如果截图包含表格生成的脚本应包含遍历行/列读取数据的示例。 - 如果截图包含模态框Modal或下拉菜单脚本应包含触发和关闭它们的操作。 - 对于表单请生成包含有效数据填充和提交的测试用例。4. 具体用户指令用户指令{instruction} 测试框架{framework}5. 少样本示例Few-Shot Learning在系统提示词中附带一两个“截图-指令-代码”的例子能显著提升模型输出的格式和逻辑一致性。例如附上一个简单的登录页面截图和对应的生成脚本示例。将以上部分组合并通过我们搭建的WEBUI传递给后端模型才能确保模型产出的脚本结构清晰、定位方式稳健、符合工程规范。3.2 模型输出解析与后处理模型返回的通常是包含代码块的Markdown文本。我们需要从中精准提取出Python代码并做初步的清理和验证。import re import ast def extract_and_validate_code(model_raw_output: str) - str: 从模型原始输出中提取Python代码并进行基础验证。 # 尝试匹配 python ... 代码块 code_block_pattern rpython\n(.*?)\n match re.search(code_block_pattern, model_raw_output, re.DOTALL) extracted_code if match: extracted_code match.group(1).strip() else: # 如果没有代码块尝试寻找看起来像Python代码的连续行 lines model_raw_output.split(\n) python_lines [line for line in lines if line.strip().startswith((import , from , def , class , page., expect., assert )) or in line] if len(python_lines) 5: # 经验阈值 extracted_code \n.join(python_lines) else: # 如果实在提取不到返回原始文本但标记问题 return # 未能提取到有效代码块请检查模型输出。\n model_raw_output # 基础语法验证可选但能提前发现明显错误 try: ast.parse(extracted_code) print(✓ 提取的代码语法验证通过。) except SyntaxError as e: print(f⚠ 提取的代码存在语法错误{e}) # 可以选择记录日志或尝试简单修复这里直接返回带错误标记的代码 extracted_code f# 语法错误警告: {e}\n extracted_code # 常见后处理确保导入了必要的库 if from playwright.sync_api import not in extracted_code and playwright in extracted_code: # 提示用户可能需要手动添加导入 extracted_code # 注意请确认已安装playwright并执行 playwright install\n extracted_code return extracted_code后处理环节还可以加入更多智能操作比如自动补全缺失的import语句、将模糊的time.sleep(5)替换成Playwright的page.wait_for_timeout(5000)、标准化断言语句格式等。这相当于一个轻量的“脚本美化器”。3.3 生成脚本的质量评估与人工修正尽管模型能力强大但当前技术下完全依赖AI生成100%准确可用的脚本仍不现实。生成后的人工审查和修正必不可少。我通常会从以下几个维度评估生成的脚本元素定位的准确性模型生成的定位器如page.get_by_text(提交)是否唯一、稳定是否容易因页面微调而失效需要将其优化为更稳健的定位方式例如使用>page.fill(input[nameusername], testuser) page.click(button:has-text(登录))经过人工优化后# 使用更精确的角色定位器并加入等待 username_input page.get_by_label(用户名) username_input.wait_for(statevisible) username_input.fill(testuser) # 使用角色定位按钮并添加成功断言 login_button page.get_by_role(button, name登录) login_button.click() # 等待导航完成并断言登录成功 expect(page).to_have_url(**/dashboard) # 使用通配符匹配URL welcome_text page.get_by_text(欢迎回来testuser) expect(welcome_text).to_be_visible()这个“生成-评估-修正”的循环是当前阶段将AI能力可靠融入测试开发工作流的关键。随着模型迭代和提示词优化需要人工干预的地方会越来越少。4. 集成与进阶打造企业级自动化流水线对于个人或小团队使用上述WEBUI进行交互式生成已经能提升效率。但要将其融入CI/CD实现规模化应用就需要进一步的集成和工程化。4.1 与测试管理平台集成我们可以将脚本生成服务封装成一个独立的微服务供测试管理平台如TestRail、Zephyr或项目管理工具如Jira调用。例如在Jira中创建一个“自动化脚本生成”的按钮关联附件中的UI截图点击后调用我们的API将生成的脚本直接回写到Jira评论或附件中并关联到对应的测试用例上。# 示例一个简单的Flask端点接收Jira webhook app.route(/jira/webhook, methods[POST]) def jira_webhook(): data request.json issue_key data[issue][key] attachment_url data[issue][fields][attachment][0][content] # 假设最新附件是截图 # 1. 从Jira下载附件 screenshot_path download_from_jira(attachment_url) # 2. 调用模型API生成脚本 script_content call_model_api(screenshot_path, 生成该界面的冒烟测试脚本) # 3. 将脚本作为评论贴回Jira post_comment_to_jira(issue_key, f生成的测试脚本\npython\n{script_content}\n) return jsonify({status: success})4.2 实现批量截图处理与脚本生成对于需要为大量页面生成测试脚本的场景如CMS后台的所有管理页面我们可以开发一个批量处理工具。其流程是使用爬虫或快照工具如Playwright本身自动遍历网站截取所有关键页面的图片。将图片批量送入脚本生成服务。对生成的脚本进行去重和合并形成一个完整的测试套件。import os from concurrent.futures import ThreadPoolExecutor import requests def batch_generate_scripts(screenshot_dir, output_dir): api_url http://localhost:8000/generate_script for img_file in os.listdir(screenshot_dir): if img_file.endswith((.png, .jpg, .jpeg)): img_path os.path.join(screenshot_dir, img_file) with open(img_path, rb) as f: files {image: f} data {instruction: 生成该页面的核心功能测试脚本, framework: playwright} resp requests.post(api_url, filesfiles, datadata) if resp.ok: script resp.json().get(script) # 保存脚本文件名与截图对应 script_filename os.path.splitext(img_file)[0] _test.py with open(os.path.join(output_dir, script_filename), w) as sf: sf.write(script) print(fGenerated script for {img_file})4.3 结合视觉回归测试生成的脚本主要用于功能流测试。我们还可以将其与视觉回归测试Visual Regression Testing结合。例如使用pixelmatch或playwright-screenshot库在脚本执行的关键步骤后截屏并与基准图对比。这样一个脚本同时完成了功能验证和UI样式校验。# 在生成的脚本中插入视觉断言 def test_login_visual(page): # ... 登录操作 ... page.goto(/dashboard) # 对登录后的仪表板进行视觉对比 screenshot page.screenshot() compare_with_baseline(dashboard.png, screenshot, threshold0.01) # 自定义对比函数5. 避坑指南与效能提升技巧在实际落地过程中我积累了一些宝贵的经验和教训能帮你少走很多弯路。5.1 提升生成脚本准确率的实战技巧提供高质量、清晰的截图这是最重要的前提。确保截图完整、分辨率足够、文字清晰。避免包含动态内容如轮播图或无关的浏览器标签、书签栏。指令尽可能具体不要只说“生成测试脚本”。要说“为这个商品列表页的搜索框和筛选器生成测试脚本覆盖搜索‘手机’和按价格排序的功能”。越具体模型越能理解你的意图。在提示词中定义“页面对象”对于复杂页面可以引导模型使用Page Object Model (POM)模式来生成脚本这样生成的代码结构更优更利于维护。请使用Page Object Model模式生成脚本。分别为登录页面LoginPage和仪表板页面DashboardPage创建类将元素定位和操作封装在其中。测试用例中调用这些页面对象的方法。迭代优化第一次生成的脚本不完美是正常的。将不理想的输出作为新的上下文告诉模型哪里不对让它修正。例如“刚才生成的脚本中登录成功的断言不准确请检查登录后页面右上角是否出现了用户头像并以此作为断言依据重新生成。”5.2 常见问题与解决方案问题1模型生成的定位器不稳定如使用绝对XPath或易变的CSS类名。解决方案在提示词中强制规定使用Playwright的语义化定位器get_by_role,get_by_text,get_by_label。如果前端代码规范可以要求模型优先使用>
基于多模态大模型的UI自动化测试脚本生成实践
发布时间:2026/6/30 18:38:06
1. 项目概述当截图“开口说话”自动化测试迎来新范式最近在折腾自动化测试时我一直在思考一个问题UI测试脚本的编写能不能再“懒”一点传统的流程要么是测试工程师对着UI设计稿或已上线的页面一行行手写定位元素和操作逻辑的代码要么是依赖录制回放工具但生成的脚本往往脆弱、难以维护一遇到UI微调就“全军覆没”。直到我开始尝试将多模态大模型MLLM引入这个流程一个全新的、高效的自动化脚本生成路径才变得清晰起来。今天要聊的这个实践就是围绕“Qwen3-VL-WEBUI”展开的核心目标很简单把一张产品UI截图“喂”给模型让它直接生成可执行、结构清晰的自动化测试脚本。这不仅仅是“用AI写代码”那么简单。它解决的是一个从视觉理解到逻辑转换的“最后一公里”问题。想象一下产品经理发来一张新功能的界面设计图或者你在回归测试中发现了一个偶现Bug并截了图。过去你需要人工解析图中的按钮、输入框、列表再转换成Selenium或Playwright的定位语句和操作命令。现在通过Qwen3-VL通义千问的多模态版本对图像内容的深度理解结合一个设计良好的WebUI交互界面我们可以让模型“看懂”截图并基于你的指令比如“为这个登录页面生成登录成功的测试用例”直接输出完整的测试脚本。这极大地降低了自动化测试的入门门槛提升了脚本创建的效率尤其适合快速迭代的敏捷开发环境和需要覆盖大量UI场景的测试需求。整个过程涉及几个关键角色Qwen3-VL作为“大脑”负责视觉识别与逻辑推理WEBUI作为“操作台”提供便捷的图像上传、指令输入和结果展示界面而最终产出的自动化测试脚本可能是基于Playwright、Selenium或Appium则是可直接投入使用的“武器”。接下来我将拆解从环境搭建、核心交互、到脚本优化落地的全流程并分享其中踩过的坑和提炼出的实战技巧。2. 核心工具链选型与部署实战工欲善其事必先利其器。要实现截图到脚本的自动化流水线我们需要搭建一个稳定、高效的工具环境。这里的选型直接决定了后续流程的顺畅度和产出脚本的质量。2.1 Qwen3-VL模型为什么是它以及如何部署在众多开源多模态大模型中我选择Qwen3-VL以Qwen2.5-VL为例主要基于以下几点考量强大的视觉问答VQA与细粒度识别能力它不仅能识别图片中的物体更能理解UI界面中的元素层级如导航栏、侧边栏、卡片、元素类型按钮、输入框、下拉菜单、表格和元素状态激活、禁用、选中。这对于准确生成操作指令至关重要。出色的代码生成与指令跟随能力作为通义千问家族成员其在代码生成任务上表现优异能够遵循“生成PythonPlaywright脚本”这类复杂指令并结构化输出。对中文UI和指令的友好支持在处理包含中文文本的界面截图时其识别准确率较高生成的脚本变量命名、注释也更符合中文开发者的习惯。灵活的部署选项支持通过Transformers库本地部署也提供了OpenAI兼容的API接口方便集成。本地部署方案推荐用于深度定制和隐私保护对于有一定GPU资源的团队本地部署能提供最好的可控性和数据安全性。以下是基于transformers和vLLM的部署步骤# 1. 创建并进入Python虚拟环境 python -m venv qwen_env source qwen_env/bin/activate # Linux/Mac # qwen_env\Scripts\activate # Windows # 2. 安装核心依赖 pip install transformers torch accelerate # 如果使用vLLM加速推理强烈推荐提升吞吐量 pip install vllm # 3. 下载模型以Qwen2.5-VL-7B-Instruct为例 # 可以从ModelScope或Hugging Face下载 from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline import torch model_id Qwen/Qwen2.5-VL-7B-Instruct tokenizer AutoTokenizer.from_pretrained(model_id, trust_remote_codeTrue) # 根据显卡显存选择加载方式 model AutoModelForCausalLM.from_pretrained( model_id, torch_dtypetorch.float16, # 半精度节省显存 device_mapauto, # 自动分配多GPU trust_remote_codeTrue ) # 使用vLLM部署以获得更高性能 from vllm import LLM, SamplingParams llm LLM(modelmodel_id, tensor_parallel_size1, gpu_memory_utilization0.9) # tensor_parallel_size为GPU数量注意Qwen3-VL系列模型对显存要求较高。7B版本至少需要16GB GPU显存才能流畅运行。如果资源有限可以考虑使用量化版本如GPTQ-Int4或使用云API方案。API服务化部署为了让WEBUI和其他服务方便调用我们需要将模型封装成API。使用FastAPI可以快速实现# server.py from fastapi import FastAPI, UploadFile, File, Form from PIL import Image import io from vllm import SamplingParams app FastAPI() # 初始化vLLM引擎 llm LLM(modelQwen/Qwen2.5-VL-7B-Instruct) app.post(/generate_script) async def generate_script( image: UploadFile File(...), instruction: str Form(请为这张UI截图生成Playwright自动化测试脚本), framework: str Form(playwright) ): # 读取并处理图片 image_data await image.read() img Image.open(io.BytesIO(image_data)) # 构建多模态提示词 # Qwen-VL的提示词需要特殊格式将图片作为消息的一部分 messages [ { role: user, content: [ {type: image, image: img}, # 传入PIL Image对象 {type: text, text: f{instruction}。请使用{framework}框架生成完整的Python测试脚本。} ] } ] # 将消息转换为模型接受的格式此处简化实际需按模型文档处理 prompt tokenizer.apply_chat_template(messages, tokenizeFalse) # 设置生成参数 sampling_params SamplingParams(temperature0.1, max_tokens2048, stop[/s]) # 推理生成 outputs llm.generate([prompt], sampling_params) generated_text outputs[0].outputs[0].text # 后处理提取代码块 import re code_pattern rpython\n(.*?)\n match re.search(code_pattern, generated_text, re.DOTALL) script_code match.group(1) if match else generated_text return {script: script_code, raw_response: generated_text}使用uvicorn server:app --host 0.0.0.0 --port 8000启动服务后我们就拥有了一个接收截图和指令返回测试脚本的API端点。2.2 WEBUI搭建Gradio vs. Streamlit的抉择有了模型API我们需要一个用户友好的界面。Gradio和Streamlit是两个热门选择我最终选择了Gradio原因如下对多模态输入的原生友好Gradio的Image组件处理图片上传和预览非常方便与模型的消息格式对接更顺畅。快速原型开发几行代码就能构建一个功能完整的交互界面迭代速度快。易于分享可以快速创建可公开访问的链接方便团队内部演示和测试。一个基础的Gradio WEBUI应用代码如下# webui_app.py import gradio as gr import requests import base64 from PIL import Image import io API_URL http://localhost:8000/generate_script # 指向刚才部署的API def generate_from_image(image, instruction, framework): if image is None: return 请先上传一张UI截图。, # 将Gradio的numpy数组或PIL Image转换为字节流 buffered io.BytesIO() image.save(buffered, formatPNG) img_bytes buffered.getvalue() # 调用后端API files {image: (ui_screenshot.png, img_bytes, image/png)} data {instruction: instruction, framework: framework} try: response requests.post(API_URL, filesfiles, datadata, timeout60) if response.status_code 200: result response.json() raw_output result.get(raw_response, ) script_code result.get(script, ) # 格式化显示 formatted_output f**生成的脚本**\npython\n{script_code}\n\n\n**模型原始回复**\n{raw_output} return formatted_output, script_code else: return fAPI调用失败状态码{response.status_code}, except Exception as e: return f请求发生错误{str(e)}, # 构建界面 with gr.Blocks(title截图转测试脚本生成器) as demo: gr.Markdown(# ️➡️ 截图转自动化测试脚本生成器) gr.Markdown(上传UI截图描述你的测试意图选择框架一键生成可运行的测试脚本。) with gr.Row(): with gr.Column(scale1): image_input gr.Image(label上传UI截图, typepil) instruction_input gr.Textbox( label测试指令, value请为这个登录页面生成一个测试脚本包含用户名密码输入和登录按钮点击。, lines3 ) framework_choice gr.Radio( choices[playwright, selenium, pytest需指定框架], valueplaywright, label测试框架 ) generate_btn gr.Button(生成脚本, variantprimary) with gr.Column(scale2): output_display gr.Markdown(label生成结果) code_output gr.Code(label纯净脚本代码, languagepython, interactiveTrue) # 绑定事件 generate_btn.click( fngenerate_from_image, inputs[image_input, instruction_input, framework_choice], outputs[output_display, code_output] ) # 添加示例 gr.Examples( examples[ [examples/login_page.png, 测试登录功能使用错误密码应提示‘密码错误’。, playwright], [examples/dashboard.png, 生成脚本验证顶部导航栏所有菜单项均可点击。, selenium], ], inputs[image_input, instruction_input, framework_choice], outputs[output_display, code_output], fngenerate_from_image, cache_examplesTrue ) if __name__ __main__: demo.launch(server_name0.0.0.0, server_port7860, shareFalse) # shareTrue可生成临时公网链接这个界面提供了图片上传、指令输入、框架选择、一键生成、结果展示含高亮代码和示例快速加载功能基本覆盖了核心操作流。2.3 测试脚本框架Playwright为何成为首选在生成的脚本框架选择上我优先推荐Playwright其次是Selenium。为什么是Playwright自动等待与稳定性Playwright内置了智能等待机制能自动等待元素可操作大大减少了因页面加载速度导致的“元素找不到”错误生成的脚本更健壮。强大的选择器引擎支持文本选择器、角色选择器等模型更容易生成准确且稳定的定位语句如page.get_by_role(button, name登录)。多语言、多浏览器支持一套脚本可在Chromium、Firefox、WebKit上运行且API设计现代简洁。录制功能可作为参考虽然我们不直接使用录制代码但Playwright Recorder生成的脚本结构清晰可以作为提示词的一部分教给模型让模型学习更地道的写法。因此在给模型的指令中我会明确要求“请使用Playwright框架生成一个完整的、可独立运行的Python测试脚本。脚本应包含必要的导入、浏览器启动、页面导航、元素操作使用get_by_role、get_by_text等稳定选择器、断言以及资源清理。”3. 从截图到可执行脚本的深度解析有了基础设施接下来我们深入核心环节模型是如何“看懂”截图并“写出”正确脚本的这个过程并非魔法其效果严重依赖于我们提供的“提示词工程”和后续的“脚本后处理”。3.1 提示词工程教会模型“如何思考”直接给模型一张图和一个简单指令它可能生成五花八门的代码。我们必须通过精心设计的提示词Prompt来约束和引导它。一个高效的提示词通常包含以下几个部分1. 角色定义与任务明确你是一名专业的自动化测试开发工程师。你的任务是根据用户提供的软件UI界面截图生成高质量、可直接执行的UI自动化测试脚本。2. 输出格式严格规定请严格按照以下格式输出 1. 脚本必须是一个完整的、可运行的Python文件。 2. 使用python ... 代码块包裹整个脚本。 3. 脚本顶部需有简要注释说明测试的功能点。 4. 使用Pytest或Unittest测试框架结构如使用def test_xxx():。 5. 对于UI元素定位优先使用Playwright的get_by_role(), get_by_text(), get_by_label()方法其次考虑get_by_test_id()。仅在必要时使用CSS或XPath。 6. 必须包含合理的等待如page.wait_for_selector和断言如expect()。 7. 脚本最后必须妥善关闭浏览器。3. 上下文信息补充关键这是提升生成准确性的核心。我们不能只给图还要告诉模型图中可能存在的“盲点”。额外注意 - 截图可能不完整或模糊请根据常见UI设计模式推断元素功能。例如一个右侧有眼睛图标的输入框通常是密码框。 - 如果截图包含表格生成的脚本应包含遍历行/列读取数据的示例。 - 如果截图包含模态框Modal或下拉菜单脚本应包含触发和关闭它们的操作。 - 对于表单请生成包含有效数据填充和提交的测试用例。4. 具体用户指令用户指令{instruction} 测试框架{framework}5. 少样本示例Few-Shot Learning在系统提示词中附带一两个“截图-指令-代码”的例子能显著提升模型输出的格式和逻辑一致性。例如附上一个简单的登录页面截图和对应的生成脚本示例。将以上部分组合并通过我们搭建的WEBUI传递给后端模型才能确保模型产出的脚本结构清晰、定位方式稳健、符合工程规范。3.2 模型输出解析与后处理模型返回的通常是包含代码块的Markdown文本。我们需要从中精准提取出Python代码并做初步的清理和验证。import re import ast def extract_and_validate_code(model_raw_output: str) - str: 从模型原始输出中提取Python代码并进行基础验证。 # 尝试匹配 python ... 代码块 code_block_pattern rpython\n(.*?)\n match re.search(code_block_pattern, model_raw_output, re.DOTALL) extracted_code if match: extracted_code match.group(1).strip() else: # 如果没有代码块尝试寻找看起来像Python代码的连续行 lines model_raw_output.split(\n) python_lines [line for line in lines if line.strip().startswith((import , from , def , class , page., expect., assert )) or in line] if len(python_lines) 5: # 经验阈值 extracted_code \n.join(python_lines) else: # 如果实在提取不到返回原始文本但标记问题 return # 未能提取到有效代码块请检查模型输出。\n model_raw_output # 基础语法验证可选但能提前发现明显错误 try: ast.parse(extracted_code) print(✓ 提取的代码语法验证通过。) except SyntaxError as e: print(f⚠ 提取的代码存在语法错误{e}) # 可以选择记录日志或尝试简单修复这里直接返回带错误标记的代码 extracted_code f# 语法错误警告: {e}\n extracted_code # 常见后处理确保导入了必要的库 if from playwright.sync_api import not in extracted_code and playwright in extracted_code: # 提示用户可能需要手动添加导入 extracted_code # 注意请确认已安装playwright并执行 playwright install\n extracted_code return extracted_code后处理环节还可以加入更多智能操作比如自动补全缺失的import语句、将模糊的time.sleep(5)替换成Playwright的page.wait_for_timeout(5000)、标准化断言语句格式等。这相当于一个轻量的“脚本美化器”。3.3 生成脚本的质量评估与人工修正尽管模型能力强大但当前技术下完全依赖AI生成100%准确可用的脚本仍不现实。生成后的人工审查和修正必不可少。我通常会从以下几个维度评估生成的脚本元素定位的准确性模型生成的定位器如page.get_by_text(提交)是否唯一、稳定是否容易因页面微调而失效需要将其优化为更稳健的定位方式例如使用>page.fill(input[nameusername], testuser) page.click(button:has-text(登录))经过人工优化后# 使用更精确的角色定位器并加入等待 username_input page.get_by_label(用户名) username_input.wait_for(statevisible) username_input.fill(testuser) # 使用角色定位按钮并添加成功断言 login_button page.get_by_role(button, name登录) login_button.click() # 等待导航完成并断言登录成功 expect(page).to_have_url(**/dashboard) # 使用通配符匹配URL welcome_text page.get_by_text(欢迎回来testuser) expect(welcome_text).to_be_visible()这个“生成-评估-修正”的循环是当前阶段将AI能力可靠融入测试开发工作流的关键。随着模型迭代和提示词优化需要人工干预的地方会越来越少。4. 集成与进阶打造企业级自动化流水线对于个人或小团队使用上述WEBUI进行交互式生成已经能提升效率。但要将其融入CI/CD实现规模化应用就需要进一步的集成和工程化。4.1 与测试管理平台集成我们可以将脚本生成服务封装成一个独立的微服务供测试管理平台如TestRail、Zephyr或项目管理工具如Jira调用。例如在Jira中创建一个“自动化脚本生成”的按钮关联附件中的UI截图点击后调用我们的API将生成的脚本直接回写到Jira评论或附件中并关联到对应的测试用例上。# 示例一个简单的Flask端点接收Jira webhook app.route(/jira/webhook, methods[POST]) def jira_webhook(): data request.json issue_key data[issue][key] attachment_url data[issue][fields][attachment][0][content] # 假设最新附件是截图 # 1. 从Jira下载附件 screenshot_path download_from_jira(attachment_url) # 2. 调用模型API生成脚本 script_content call_model_api(screenshot_path, 生成该界面的冒烟测试脚本) # 3. 将脚本作为评论贴回Jira post_comment_to_jira(issue_key, f生成的测试脚本\npython\n{script_content}\n) return jsonify({status: success})4.2 实现批量截图处理与脚本生成对于需要为大量页面生成测试脚本的场景如CMS后台的所有管理页面我们可以开发一个批量处理工具。其流程是使用爬虫或快照工具如Playwright本身自动遍历网站截取所有关键页面的图片。将图片批量送入脚本生成服务。对生成的脚本进行去重和合并形成一个完整的测试套件。import os from concurrent.futures import ThreadPoolExecutor import requests def batch_generate_scripts(screenshot_dir, output_dir): api_url http://localhost:8000/generate_script for img_file in os.listdir(screenshot_dir): if img_file.endswith((.png, .jpg, .jpeg)): img_path os.path.join(screenshot_dir, img_file) with open(img_path, rb) as f: files {image: f} data {instruction: 生成该页面的核心功能测试脚本, framework: playwright} resp requests.post(api_url, filesfiles, datadata) if resp.ok: script resp.json().get(script) # 保存脚本文件名与截图对应 script_filename os.path.splitext(img_file)[0] _test.py with open(os.path.join(output_dir, script_filename), w) as sf: sf.write(script) print(fGenerated script for {img_file})4.3 结合视觉回归测试生成的脚本主要用于功能流测试。我们还可以将其与视觉回归测试Visual Regression Testing结合。例如使用pixelmatch或playwright-screenshot库在脚本执行的关键步骤后截屏并与基准图对比。这样一个脚本同时完成了功能验证和UI样式校验。# 在生成的脚本中插入视觉断言 def test_login_visual(page): # ... 登录操作 ... page.goto(/dashboard) # 对登录后的仪表板进行视觉对比 screenshot page.screenshot() compare_with_baseline(dashboard.png, screenshot, threshold0.01) # 自定义对比函数5. 避坑指南与效能提升技巧在实际落地过程中我积累了一些宝贵的经验和教训能帮你少走很多弯路。5.1 提升生成脚本准确率的实战技巧提供高质量、清晰的截图这是最重要的前提。确保截图完整、分辨率足够、文字清晰。避免包含动态内容如轮播图或无关的浏览器标签、书签栏。指令尽可能具体不要只说“生成测试脚本”。要说“为这个商品列表页的搜索框和筛选器生成测试脚本覆盖搜索‘手机’和按价格排序的功能”。越具体模型越能理解你的意图。在提示词中定义“页面对象”对于复杂页面可以引导模型使用Page Object Model (POM)模式来生成脚本这样生成的代码结构更优更利于维护。请使用Page Object Model模式生成脚本。分别为登录页面LoginPage和仪表板页面DashboardPage创建类将元素定位和操作封装在其中。测试用例中调用这些页面对象的方法。迭代优化第一次生成的脚本不完美是正常的。将不理想的输出作为新的上下文告诉模型哪里不对让它修正。例如“刚才生成的脚本中登录成功的断言不准确请检查登录后页面右上角是否出现了用户头像并以此作为断言依据重新生成。”5.2 常见问题与解决方案问题1模型生成的定位器不稳定如使用绝对XPath或易变的CSS类名。解决方案在提示词中强制规定使用Playwright的语义化定位器get_by_role,get_by_text,get_by_label。如果前端代码规范可以要求模型优先使用>