ADB模拟点击结合GLM-4.6V-Flash-WEB实现自动化测试
在移动应用测试领域,一个老生常谈的问题始终困扰着工程师:当UI界面频繁变更、按钮是图片而非文本、或者控件没有唯一ID时,传统的自动化脚本动辄失效。我们写了一堆基于XPath或ResourceId的定位逻辑,结果产品改了个图标,整个流程就断了——这种“脆弱性”让自动化测试常常沦为一次性工具。
有没有可能让测试系统像人一样“看懂屏幕”,然后做出操作决策?比如,你告诉它:“点那个红色的登录按钮”,它就能根据当前画面判断哪里是登录按钮,并准确点击?这正是多模态大模型带来的新范式。而今天我们要聊的,就是如何用GLM-4.6V-Flash-WEB这个轻量级视觉语言模型,配合最基础的ADB模拟点击,构建一套真正具备“视觉感知+智能决策+自动执行”能力的自动化测试链路。
这套方案不依赖复杂的SDK,也不需要被测应用做任何改造,甚至能在游戏界面上运行。它的核心思路很简单:
截图 → 问模型“该点哪” → 解析答案 → 转换坐标 → ADB点击 → 循环。
让机器“看懂”屏幕:GLM-4.6V-Flash-WEB 的实战价值
GLM-4.6V-Flash-WEB 是智谱AI推出的轻量化多模态模型,专为高并发、低延迟场景设计。和那些动不动要多卡部署的“大块头”不同,它可以在单张RTX 3090上稳定运行,推理速度接近传统OCR,但理解能力远超规则引擎。
它的底层架构依然是Transformer,采用ViT作为图像编码器,将输入图像切分为patch后提取特征,再与文本指令通过交叉注意力机制融合。最终输出自然语言回答,比如:
“图中存在‘登录’按钮,位于右下角,大致坐标范围为x=800~900,y=1200~1300。”
这意味着我们不再需要预先知道控件的ID或文字内容,只要能用自然语言描述目标,模型就能从像素中找出对应区域。
为什么选它而不是其他模型?
| 维度 | OCR+正则匹配 | GLM-4V(通用版) | GLM-4.6V-Flash-WEB |
|---|---|---|---|
| 推理速度 | 极快 | 慢(500ms+) | 快(<150ms) |
| 部署成本 | 极低 | 高(需A100或多卡) | 低(单卡消费级GPU即可) |
| 多语言支持 | 弱 | 强 | 强 |
| 语义理解能力 | 无 | 强 | 较强(支持few-shot推理) |
| 实际可用性 | 场景受限 | 延迟高难集成 | 适合高频调用的自动化系统 |
如果你的目标是在CI/CD流水线里每分钟跑几十次测试,那响应速度和资源占用比绝对精度更重要。GLM-4.6V-Flash-WEB 正好卡在这个平衡点上。
如何调用?一个极简Python示例
假设你已经通过Docker或本地服务启动了GLM-4.6V-Flash-WEB的Web API(例如监听http://localhost:8080/generate),下面这段代码就可以完成一次“视觉问答”:
import requests from PIL import Image import base64 import io def image_to_base64(image_path): with open(image_path, "rb") as f: return base64.b64encode(f.read()).decode() def query_vlm(image_path, prompt): url = "http://localhost:8080/generate" payload = { "image": image_to_base64(image_path), "prompt": prompt, "max_tokens": 100 } headers = {"Content-Type": "application/json"} try: response = requests.post(url, json=payload, headers=headers, timeout=10) if response.status_code == 200: return response.json().get("text", "").strip() else: return f"Error: {response.status_code}, {response.text}" except Exception as e: return f"Request failed: {str(e)}" # 示例使用 result = query_vlm("/root/screenshots/login_page.png", "请分析图片,是否存在‘立即购买’按钮?如果存在,请描述其位置。") print("模型回复:", result)典型输出可能是:
“存在‘立即购买’按钮,颜色偏红,位于屏幕右侧中部,估计中心坐标约为(920, 760)。”
接下来的任务,就是把这个“语义描述”转化为可执行的像素坐标。
从“看到”到“做到”:ADB模拟点击的本质
很多人以为自动化测试一定要用Appium或者UiAutomator,其实最原始也最稳定的手段,反而是ADB的input tap命令。
ADB全称Android Debug Bridge,是Android平台的标准调试工具。只要你开启了USB调试模式,PC就能通过adb shell向设备发送指令。其中:
adb shell input tap 540 960这一行命令就会在设备上触发一次触摸事件,系统会生成完整的MotionEvent序列(ACTION_DOWN → 短暂延迟 → ACTION_UP),和真实手指点击几乎无异。
它凭什么比Appium更轻快?
| 方案 | 是否需要安装客户端 | 启动耗时 | 控制粒度 | 学习成本 | 兼容性 |
|---|---|---|---|---|---|
| ADB input tap | 否 | <100ms | 坐标级 | 极低 | 所有Android设备 |
| Appium | 是(WebDriverAgent) | >3s | 控件级 | 高 | 主流APP |
| Auto.js(JS脚本) | 是 | ~1s | 图像识别+坐标 | 中 | 需Root或无障碍权限 |
ADB的最大优势在于“零侵入”。你不需在App里集成任何测试框架,也不依赖AccessibilityService,只要设备连上了,就能操作。
封装成Python函数,让它更好用
为了便于程序化调用,我们可以把ADB命令封装成一个带错误处理和多设备支持的函数:
import subprocess import time def adb_tap(x, y, device_id=None): """ 模拟点击指定坐标 :param x: 横坐标 :param y: 纵坐标 :param device_id: 设备序列号(多设备时必填) """ cmd = ["adb"] if device_id: cmd += ["-s", device_id] cmd += ["shell", f"input tap {x} {y}"] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=5) if result.returncode == 0: print(f"✅ 成功点击 ({x}, {y})") return True else: print(f"❌ 点击失败: {result.stderr}") return False except Exception as e: print(f"⚠️ 执行异常: {e}") return False # 使用示例 adb_tap(540, 960) time.sleep(1) adb_tap(200, 300)这个函数可以轻松集成进你的测试主循环中,配合截图和模型推理形成闭环。
整体工作流:构建“视觉驱动”的自动化流水线
整个系统的运作流程可以用一张结构图来概括:
graph TD A[获取当前屏幕截图] --> B[构造自然语言问题] B --> C[发送至GLM-4.6V-Flash-WEB] C --> D[解析模型返回文本] D --> E[提取建议坐标] E --> F[映射为实际像素位置] F --> G[执行ADB点击] G --> H{是否达成目标?} H -- 否 --> A H -- 是 --> I[测试通过]每一步都值得细究:
1. 截图采集
使用ADB原生命令抓取当前屏幕:
adb exec-out screencap -p > screen.png注意要用exec-out而不是pull,前者直接输出二进制流,避免因文件路径权限导致失败。
2. 问题构造
根据测试用例动态生成提问文本。例如:
- “请识别图中‘跳过广告’按钮的位置。”
- “当前页面是否有弹窗?如果有,请指出关闭按钮的大致坐标。”
- “查找带有购物车图标的按钮,并返回其位置。”
这类提示词的设计很关键。实验表明,加入“大致”、“估计”、“如果存在”等缓和语气,能显著降低模型产生“幻觉”的概率。
3. 结果解析
模型返回的是自然语言,我们需要从中抽取出坐标信息。可以通过正则匹配实现:
import re def extract_coordinates(text): # 匹配如 "(800, 1200)" 或 "x=800, y=1200" 的格式 patterns = [ r'\(?(\d+),\s*(\d+)\)?', # (x, y) r'x[=:]\s*(\d+)[,\s]*y[=:]\s*(\d+)' # x=..., y=... ] for pattern in patterns: match = re.search(pattern, text) if match: return int(match.group(1)), int(match.group(2)) return None # 示例 coord = extract_coordinates("中心位置大约在(920, 760)") print(coord) # 输出: (920, 760)当然,也可以训练一个小的NER模型来做更精准的信息抽取,但对于大多数场景,正则已足够。
4. 坐标归一化与校准
不同设备分辨率各异。比如你在1080×1920的手机上训练出的坐标逻辑,在2K屏上就会偏移。解决方案是统一转换为相对坐标:
def absolute_to_relative(x, y, width=1080, height=1920): return round(x / width, 3), round(y / height, 3) def relative_to_absolute(rel_x, rel_y, cur_width, cur_height): return int(rel_x * cur_width), int(rel_y * cur_height)这样即使换设备,也能保持行为一致。
工程实践中的关键考量
虽然原理简单,但在真实项目中仍有不少坑需要注意。
✅ 最佳实践建议
- 控制截图频率:连续高频截图会导致设备CPU飙升,建议每次操作后等待至少1秒再截新图。
- 启用结果缓存:对相同界面状态(可通过图像哈希判断)缓存模型输出,避免重复请求。
- 添加重试机制:点击后检查界面是否变化(再次截图比对),若未响应则重新识别或跳过。
- 日志可视化:保存每轮的截图、模型输入输出、执行动作,方便回溯调试。
⚠️ 潜在风险提醒
- 模型幻觉:VLM有时会“编造”不存在的按钮,建议设置置信度过滤,或结合多个提示词交叉验证。
- 网络延迟:若模型部署在远程服务器,API延迟可能成为瓶颈,优先考虑本地化部署。
- 权限配置:确保ADB调试已开启,且授权列表中包含当前主机。
- 合规边界:仅用于合法测试目的,禁止用于刷量、抢购等违反用户协议的行为。
它能解决什么问题?
这套组合拳特别适合应对以下几类棘手场景:
- 图像按钮识别:传统工具无法处理只有图标没有文字的按钮,而VLM可以通过形状、颜色、上下文综合判断。
- UI频繁变更:产品天天改版,XPath天天失效?现在只需修改一句自然语言指令即可适配。
- 多语言适配测试:同一功能在中文显示“登录”,英文变成“Sign In”,但你只需要说“click the login button”,模型都能理解。
- 动态加载内容:H5页面、游戏UI、动画过渡中的元素,只要截图时机得当,就能捕捉并操作。
更重要的是,得益于开源镜像和一键部署脚本(如项目中的/root/1键推理.sh),开发者几分钟内就能搭起原型环境,快速验证想法。
这种“视觉认知 + 设备控制”的模式,正在重新定义自动化测试的可能性。它不再依赖静态的UI树结构,而是模仿人类的操作直觉——先看一眼,再决定怎么点。随着边缘计算能力和小型化模型的进步,未来我们或许能在手机端直接运行类似的轻量VLM,实现实时、离线、自适应的智能交互代理。
而现在,你只需要一块安卓设备、一个GPU、一段Python脚本,就已经站在了这场变革的起点。