我们AI测试年度社群很多小伙伴让我讲讲APP原生怎么测,现在我就把付费社群里分享的落地实战方法分享给大家。点下方链接关注,持续跟踪精彩内容。
AI 能不能真正把 APP UI 自动化测试做起来?
不是跑一个登录 Demo,也不是让 AI 随便点几个按钮,而是真正跑通一条完整的电商核心链路,比如:
登录、浏览商品、进入详情、加入购物车、结算、提交订单、支付、查询订单结果。

最近我基于 Midscene + Android 真机 搭了一套 APP UI 自动化框架,也踩了不少坑。
包括 Midscene 框架怎么搭、视觉模型怎么配置、Playground 怎么调试、真机切页为什么会模糊,以及真正进入项目后最关键的几个问题:
AI 自动化用例应该怎么设计? 为什么不能把整条业务链路一次性交给 aiAct? 购物车有历史商品时怎么避免选错? 如何让多个自动化步骤之间传递业务上下文? 怎么减少视觉模型调用,提高执行效率?
这篇文章把这套实践完整梳理一下。
一、为什么我开始尝试 Midscene 做 APP 自动化?
传统 APP UI 自动化,大家比较熟悉的是 Appium、uiautomator2 等方案。
它们有一个非常典型的特点:依赖元素定位。
比如通过 id、xpath、text、accessibility 等方式找到控件,然后执行点击、输入等操作。
这类方案的优点是确定性比较强,但维护成本也很明显。页面结构一旦调整,原来的元素定位就可能失效,随之而来的就是脚本维护。
Midscene 的思路不太一样。
它更偏向于:
通过自然语言描述测试目标,由 AI 理解当前页面,再找到对应控件并执行操作。
例如:
点击首页的分类按钮或者:
进入商品列表,选择第一个普通在售商品
从使用体验来看,它最大的变化是:
过去我们主要告诉自动化框架“元素在哪里”,现在可以更多地告诉 AI “我要完成什么业务动作”。
但真正做下来之后,我发现一个很重要的问题:
AI 自动化并不是把整条测试用例扔给 AI 就完事了。
真正决定稳定性的,依然是测试设计和工程设计。
二、先把 Midscene APP 自动化框架搭起来
我的项目采用的是:
Vitest + Midscene + Android 真机 + TypeScript
项目结构可以简单理解成:
ecommerce-ui-e2e│├─ src│ ├─ fixtures│ ├─ prompts│ └─ utils│├─ e2e│ └─ android│├─ scripts│├─ .env├─ package.json└─ vitest.config.ts在框架设计上,我比较推荐一个原则:
代码负责流程控制,Prompt 负责业务动作。
比如登录、选商品、加入购物车、提交订单这些核心流程由 TypeScript 控制,而“怎么在当前页面完成登录”“怎么选择普通在售商品”这些操作细节交给 Prompt。
这样做的好处是,当页面发生变化时,很多情况下只需要调整 Prompt,而不用重新改整套测试流程。
三、配置视觉模型和被测 APP
Midscene 本身负责页面理解和自动化操作,但背后还需要视觉模型支持。
我这里使用的是火山方舟视觉模型。

配置可以统一放在 .env 中:
MIDSCENE_MODEL_BASE_URL="https://ark.cn-beijing.volces.com/api/v3"MIDSCENE_MODEL_API_KEY="你的 API_KEY"MIDSCENE_MODEL_NAME="你的 Endpoint"MIDSCENE_MODEL_FAMILY="doubao-seed"MIDSCENE_PREFERRED_LANGUAGE="Chinese"APP 配置:
APP_PACKAGE="uni.app.UNIA88067B"APP_ACTIVITY="io.dcloud.PandoraEntry"APP_NAME="AItest"APP_LAUNCH_WAIT_MS="6000"测试账号同样统一配置:
APP_LOGIN_PHONE="13912345678"APP_LOGIN_PASSWORD="123456"这里有一个很基础但很重要的工程习惯:
模型 Key、测试账号、密码不要直接写死在测试代码中。
统一通过环境变量管理,并把 .env 加入 .gitignore,避免敏感信息进入代码仓库。
四、正式写用例前,先用 Playground 调 Prompt
Midscene 有一个非常适合调试的工具:Playground。
Android 环境下,可以通过下面的方式启动:
cd D:\MyTest-APP\MyTest\ecommerce-ui-e2enpm run playground:android
启动后连接 Android 真机,就可以直接输入自然语言控制 APP。

例如:
点击首页分类选择第一个商品进入详情页点击加入购物车我现在通常会先在 Playground 里完成三件事。
1. 验证 Prompt 是否能被正确理解
正式写入自动化代码之前,先确认模型是否能稳定理解这句话。
如果 Playground 里都经常执行错,写进自动化脚本后一般也不会变稳定。
2. 调整业务描述
比如一句:
点击优惠券如果 AI 容易找错,可以增加业务上下文:
点击结算页面中的优惠券一栏Prompt 并不是越长越好,而是要把当前动作所需的关键信息说清楚。
3. 找到稳定的操作方式
传统自动化通常是先写代码,再运行调试。
Midscene 更适合先在 Playground 验证自然语言操作方式,确认稳定后,再把 Prompt 沉淀到正式测试框架里。
这个过程会省掉很多无效调试时间。
五、Playground 真机切页为什么会模糊?
这个问题我一开始也踩过坑。
使用 Midscene Playground 查看 Android 真机时,我发现页面切换瞬间经常会变模糊,但使用官方 scrcpy 查看同一台设备却比较流畅。
进一步对比后发现,两者的视频预览机制并不完全一样。
其中一个比较关键的问题是视频数据原来使用了:
socket.volatile.emit('video-data')volatile 的特点是:当接收端来不及处理数据时,部分帧允许直接丢弃。
在静止页面下问题不明显,但快速从首页切到商品列表、再进入详情页时,中间帧一旦被丢掉,就容易出现瞬间模糊。
我的处理主要有两部分。
首先提高视频参数:
maxSize: 0同时提高视频码率和 FPS。
更关键的是,把:
socket.volatile.emit('video-data')调整为:
socket.emit('video-data')减少快速切页时主动丢帧。
修改之后,Playground 的页面切换效果明显稳定很多。
六、不要直接改完 node_modules 就算了
如果只是手工修改 node_modules,下次重新执行 npm install,这些修改很可能全部丢失。
所以我后来把这部分处理做成了补丁脚本:
scripts/patch-midscene-scrcpy-fps.mjs并提供命令:
npm run patch:scrcpy-fps同时挂到 postinstall。
这样重新安装依赖之后,补丁也能自动恢复。
这件事情本身不复杂,但背后其实是一个很重要的工程思路:
能够脚本化的问题,就不要依赖人的记忆。
自动化测试框架不只是把用例跑起来,还应该尽量降低环境维护成本。
七、Midscene 用例最容易犯的错误:一个 Prompt 跑完整条链路
刚开始接触 Midscene 时,很容易写出类似这样的 Prompt:
打开 APP,如果没有登录就完成登录,进入首页,点击分类,选择一个商品,加入购物车,进入购物车,选择刚才的商品,完成结算,填写地址,使用优惠券,提交订单,完成支付,然后查询订单。看起来非常智能。
实际上真实项目里,我非常不建议这么做。
主要有三个问题。
第一,任务太长
AI 不仅要理解当前页面,还要记住后面十几个步骤。
页面状态稍微出现变化,后面的执行就容易受到影响。
第二,失败后不好定位
如果一个 Prompt 包含十几个动作,最终失败时,很难快速判断到底是选商品错了、购物车选错了,还是提交订单这一步出了问题。
第三,很难复用
登录、选商品、购物车、结算,本来就是不同的业务能力。
如果全部写死在一个 Prompt 里,后续其他测试场景基本无法直接复用。
所以目前我更推荐:
把完整业务链路拆成多个业务动作,由代码负责流程编排,AI 负责理解页面并完成当前动作。
这是我认为使用 Midscene 时非常重要的设计原则。
八、一条电商主链路应该怎么拆解?
例如完整核心链路包含:
登录、选商品、加入购物车、购物车结算、提交订单、支付、查询订单。
代码负责控制这些业务步骤的先后顺序,而每一个具体动作,再交给对应 Prompt。
例如登录 Prompt:
判断当前是否已经登录。如果已经登录,不要退出,不要重复登录。如果未登录,则使用测试账号完成登录。选商品:
从分类进入商品列表。选择第一个普通在售商品。不要使用搜索。购物车结算:
进入购物车。购物车中可能存在历史商品。只选择本次加入购物车的商品进行结算。这里有一个很重要的变化:
Prompt 不再负责整个测试用例,而只负责一个明确的业务动作。
这和我们写代码时强调的“单一职责”其实很像。
九、登录场景不要每次都重新登录
APP 自动化环境里经常会保留登录态。
如果每次都简单告诉 AI:
登录测试账号就有可能出现一种很低效的情况:
用户明明已经登录,AI 却先退出账号,再重新进入登录页面完成登录。
所以更合适的 Prompt 不是“完成登录”,而是先判断状态:
判断当前是否已经登录。如果已经登录则保持当前状态,不要退出或重新登录。只有在未登录情况下才完成登录。这类设计体现了 AI 自动化一个很重要的思路:
告诉 AI 业务目标和约束,而不是机械规定每一步点击动作。
十、购物车历史数据怎么处理?
电商自动化里有一个很现实的问题:
购物车不一定是干净的。
比如测试开始之前,购物车里已经存在商品 A、商品 B 和商品 C,本次测试又新增了商品 D。
如果 Prompt 只是:
进入购物车并结算AI 很可能把多个商品一起结算。
这就引出了一个我认为非常重要的概念:
业务锚点
业务锚点,是在自动化流程中用来唯一识别某个业务对象的数据。
它不是 UI 元素,也不是 xpath,而是业务属性。
例如商品可以通过下面这些属性进行识别:
{"productName":"华为智能手机","spec":"256G 黑色","price":"3999","quantity":"1"}商品加入购物车之后,我们可以先通过 aiQuery 提取这些信息并保存下来。
等进入购物车之后,再告诉 AI:
找到商品名称、规格与刚才记录一致的商品,只选择该商品进行结算。这样自动化关注的不再是“页面上的第几个商品”,而是“本次业务流程真正操作的是哪个商品”。
对于真实业务场景来说,这比依赖页面位置稳定得多。
十一、不要只会用 aiAct,aiQuery 同样重要
很多刚开始使用 Midscene 的同学,会把注意力全部放在 aiAct 上。
其实在完整自动化流程中,aiQuery 同样重要。
它适合做一件事:
从当前页面提取结构化业务信息。
比如加入购物车后,可以提取商品名称、规格、价格;订单提交之后,可以继续提取订单号、订单金额和订单状态。
这些数据保存下来之后,又可以提供给后续步骤继续使用。
这样多个 AI 操作之间,就不再是完全独立的动作,而是有了业务上下文。
我自己比较推荐这种思路:
AI 负责操作页面,aiQuery 负责提取关键业务数据,代码负责保存和传递这些数据。
这也是前面“业务锚点”能够成立的基础。
十二、Prompt 和代码最好分开管理
项目规模小的时候,把 Prompt 直接写在 TypeScript 中问题不大。
但业务场景越来越多之后,维护会越来越麻烦。
所以我更倾向于把它们拆开:
tests/ core-chain.tsprompts/ login.yaml select-product.yaml cart-checkout.yaml submit-order.yamlTypeScript 管理核心测试流程,YAML 管理具体业务动作。
例如代码关心的是:
login()selectProduct()addToCart()checkout()submitOrder()至于“怎么选商品”“怎么判断登录状态”,由 Prompt 自己维护。
这样一来,流程逻辑和 AI 指令不会全部堆在一个文件里,可读性和维护性都会好很多。
十三、Midscene 当前一个很现实的问题:执行速度
AI 驱动 UI 自动化目前有一个客观问题:
通常会比传统定位式自动化慢。
因为传统自动化大多直接定位元素并执行,而 AI 自动化还需要处理截图、模型理解、任务规划等过程。
所以优化 Midscene 执行效率时,不能只想着换一个更快的模型。
很多时候,用例设计本身才是最大的优化空间。
我目前主要从下面几个方面处理。
1. 减少不必要的 aiAct
并不是动作拆得越细越好。
例如下面三个动作:
点击分类 选择商品 进入商品详情
如果模型表现稳定,可以合并成:
从分类进入商品列表,选择第一个普通在售商品进入详情页。核心原则不是“越细越稳定”,而是:
找到稳定性和执行效率之间的平衡点。
2. 减少固定等待
不要每操作一步都简单地 sleep 5 秒。
真正应该等待的是页面状态,例如页面加载完成、弹窗出现、按钮可操作。
一条长链路中,如果每一步都多等几秒,最终执行时间会被明显拉长。
3. 减少无价值断言
也不需要每点击一次就断言一次。
更值得验证的是关键业务节点,比如:
是否登录成功 选择的商品是否正确 订单是否提交成功 订单号是否生成 支付金额是否正确
UI 自动化的目标不是证明“每一个按钮都被点过”,而是证明核心业务链路能够正确完成。
十四、能用 Instant Action,就不要所有动作都交给 LLM
Midscene 中还有一类很值得利用的能力:
Instant Actions。
例如 aiTap()、aiInput() 这类操作,适合处理目标明确的点击和输入。
如果我们已经非常明确当前要点击什么、输入什么,就不一定需要再让大模型完成复杂规划。
例如:
选择第一个普通在售商品这个动作涉及判断商品状态、识别商品类型,适合交给 AI 理解。
但如果当前页面只有一个明确的“确认”按钮,那么直接进行确定性操作往往更高效。
所以我现在比较认可一个原则:
明确、确定的操作尽量直接执行;需要理解页面和业务语义的地方,再交给 AI。
这样既能利用 AI 的优势,也不会因为过度调用模型拖慢整条链路。

十五、我理解的 AI UI 自动化架构
经过这段时间实践,我越来越觉得:
AI UI 自动化并不意味着“所有事情全部交给 AI”。
更合理的方式应该是几种能力协同:
代码负责核心流程、条件判断和数据传递; AI负责页面理解以及不确定场景下的判断; Instant Action负责确定性较高的点击和输入; aiQuery负责提取商品、订单、金额等业务数据; 业务锚点负责保持上下文一致; 测试报告负责失败分析和问题定位。
真正稳定的 AI 自动化框架,本质上仍然需要工程化设计。
AI 只是其中非常重要的一层能力,而不是整个自动化框架本身。
十六、最后说说测试报告
自动化执行完成以后,只知道“成功”或者“失败”是不够的。
尤其是在 AI 自动化里,我们更加关心:
当前执行到了哪个业务步骤; AI 当时识别到的是什么页面; 实际执行了什么操作; 问题发生在哪一步; 失败时页面的真实状态是什么。
Midscene 的测试报告在这方面比较有价值。
因为 AI 自动化失败时,原因可能很多。
可能是模型理解错了,也可能是页面没有加载完成;可能是 Prompt 写得不够准确,也可能是环境里存在历史测试数据,甚至有可能真的是业务 Bug。
所以对于 AI UI 自动化来说:
能不能快速解释“为什么失败”,和“能不能自动执行”同样重要。
这也是我越来越看重报告和可观测性的原因。
写在最后
最近做 Midscene APP 自动化,我最大的感受是:
AI 并没有让自动化测试工程变简单,反而让测试设计变得更加重要。
过去我们更多考虑元素怎么定位、Page Object 怎么封装、代码怎么复用。
到了 AI 自动化阶段,又多了一些新的问题:
AI 和代码应该怎么分工?Prompt 应该拆到什么粒度?多个步骤之间怎么传递上下文?业务对象如何准确识别?哪些动作值得调用大模型,哪些动作直接执行反而更好?
所以真正值得学习的,并不是简单掌握一个 aiAct。
更重要的是建立一套:
稳定、可维护、可复用,同时执行效率可接受的 AI 自动化测试设计方法。
目前我比较认可的原则是:
代码负责流程,AI 负责理解;Prompt 负责业务动作,业务锚点负责上下文;确定性的事情直接做,不确定的事情再交给 AI。
这可能也是未来 AI 驱动 UI 自动化真正进入企业项目后,对测试工程师最有价值的一种能力。
看到这里如果你觉得有帮助,欢迎点赞、在看、转发!
推荐阅读
太幸福了吧!Playwright 官方推出这三个Agent,轻松搞定AI测试全流程,建议转发收藏(附详细操作步骤)
用Skills+知识图谱实现多接口串联测试,简直王炸(建议转发收藏)
一个测试人必备的需求分析Skill,搞定需求分析8大维度,生成用例采纳率直接拉满
太幸福了,10万行代码接口测试,我用知识图谱+Skill 让 AI 半小时全干完了,一学就会
太幸福了吧!Playwright 官方推出这三个Agent,轻松搞定AI测试全流程,建议转发收藏(附详细操作步骤)
一个测试人必备的需求分析Skill,搞定需求分析8大维度,生成用例采纳率直接拉满
3个测试Skills,让工作效率直接拉满!从入门到工程化全覆盖
测试人必会Skills:接口文档AI快速生成(附详细步骤,建议收藏)
夜雨聆风