- 复盘:编排设计、逐轮决策链、35 项修复全量清单、关键技术点、踩坑与复用流程 - 真机指南:闭环模型(注入/截图/dumpLayout/hilog 三通道)、命令手册、坐标库、 安全访问选择器等 7 条踩坑、成本红线、标准测试脚本骨架
15 KiB
真机闭环自动化测试 · 实践指南
日期:2026-08-30 | 适用:Windows 开发机 + Git Bash + HarmonyOS 真机(本项目实测机:
88Z9K26525009283,HarmonyOS,DevEco SDK 在默认安装路径) 本文是《五轮多Agent评审迭代-复盘与追溯.md》的姊妹篇:那篇讲"做了什么",这篇讲**"真机自动化闭环到底怎么搭、每条命令怎么用、哪些坑会让人白干一小时"**,目标是照抄即可复现。
0. 什么是本文说的"闭环"
注入输入(uitest uiInput) ──→ App/系统界面变化
↑ │
│ 观察三通道:
│ ① 截图 snapshot_display(视觉证据,留档)
│ ② UI 树 uitest dumpLayout(结构化文本,可 grep 坐标)
│ ③ hilog(App 内打点,判定逻辑走到哪一步)
│ │
判定(截图→视觉模型描述 / UI 树→精确文本+坐标 / hilog→时序事实)
└──── 决定下一次注入 ←──────────┘
三通道缺一不可:截图给人看和留档,UI 树给机器精确定位(视觉模型对中文文案有幻觉,坐标和文字必须以 UI 树为准),hilog 区分"界面没变"是"代码没执行"还是"执行了但渲染没刷新"(本次迭代靠它定位了照片链 P0)。
1. 环境准备(一次性)
1.1 工具链路径
# 每个新 Git Bash 会话先执行(建议写进 ~/.bashrc)
export PATH="$PATH:/c/Program Files/Huawei/DevEco Studio/sdk/default/openharmony/toolchains"
export MSYS_NO_PATHCONV=1 # 关键!见 §5.1
hdc:<DevEco>/sdk/default/openharmony/toolchains/hdc- 构建门禁:
cd /d/meida/app && cmd //c .build-gate.bat(产物entry/build/default/outputs/default/entry-default-signed.hap,已签名)
1.2 设备
hdc list targets # 应输出序列号,如 88Z9K26525009283
设备必须解锁亮屏。锁屏(PIN)会挡住一切交互注入——本次迭代 R1~R3 三轮测试全被 PIN 锁阻塞,只完成了"装包+启动+hilog 无崩溃"这层。屏幕熄灭后可用:
hdc -t <SN> shell "power-shell wakeup" # 亮屏(无锁时直接到桌面)
hdc -t <SN> shell "uitest uiInput keyEvent 2" # 上滑/唤醒手势(keyEvent 2 = 电源键语义场景之一,按设备实测)
有 PIN 的设备远程解不了锁(BouncerView 挡住),需要人手动解锁。这是当前方案的硬边界。
1.3 后端(App 登录/配额/报告链路需要)
后端跑在 WSL(Windows 侧 uv 管不了 WSL 建的 venv,必须进 WSL):
# 拉起(make up 起 PG/Redis → migrate → run;详见 meida_backend/README.md)
wsl -e bash -lc "cd /mnt/d/meida/meida_backend && make up && make migrate && make run"
# 健康检查(后端实际监听 0.0.0.0:8666)
wsl -e bash -lc "curl -s -m 2 http://127.0.0.1:8666/api/v1/health || echo DOWN"
真机连后端:真机访问开发机的局域网 IP(App 登录页可填服务地址;DEV_AUTH_ENABLED=true 时用 dev-token 登录)。Windows 侧验证转发是否通:curl http://127.0.0.1:8666/api/v1/health。
单测(不连库不发网络,安全):
wsl -e bash -lc "cd /mnt/d/meida/meida_backend && uv run --extra dev pytest tests/unit --no-cov -q"
# 注意 --extra dev:pytest 在 dev 依赖组里,不带会 spawn 失败
2. 核心命令手册
以下全部带 -t <SN>(多设备安全);单设备可省。<SN> = 88Z9K26525009283。
2.1 装包与启停(★ 有一个致命细节)
# 构建并安装
cd /d/meida/app && cmd //c .build-gate.bat
hdc -t <SN> install -r "D:\meida\app\entry\build\default\outputs\default\entry-default-signed.hap"
# 启动
hdc -t <SN> shell "aa start -a EntryAbility -b com.xiangsilian.app"
# ★★★ 验证新代码前必须重启进程:install -r 只替换包文件,不杀正在运行的进程!
# aa start 对已运行的 App 只是拉到前台——你测的还是旧代码(R5 热修当场踩过)
hdc -t <SN> shell "ps -ef | grep xiangsilian | grep -v grep" # 看进程启动时间戳甄别新旧
hdc -t <SN> shell "aa force-stop com.xiangsilian.app" # 杀
hdc -t <SN> shell "aa start -a EntryAbility -b com.xiangsilian.app" # 再启 → 这才是新包
# 杀图库(清"安全访问选择器"的残留蒙层,见 §5.3)
hdc -t <SN> shell "aa force-stop com.huawei.hmos.photos"
标准节奏:build → install -r → force-stop → start → 验证。
2.2 输入注入
hdc -t <SN> shell "uitest uiInput click 576 2340" # 点击(像素坐标)
hdc -t <SN> shell "uitest uiInput longClick 576 2340" # 长按(注意:是 longClick 不是 longPress)
hdc -t <SN> shell "uitest uiInput doubleClick 576 2340"
hdc -t <SN> shell "uitest uiInput swipe 576 2000 576 800 600" # 滑动(velocity 200~40000)
hdc -t <SN> shell "uitest uiInput keyEvent Back" # 返回键(也支持 Home/Power/数字)
hdc -t <SN> shell "uitest uiInput inputText 576 1200 hello" # 对已聚焦输入框打字
打错命令时 uitest 会打印完整 usage(比猜语法快)。
2.3 观察通道一:截图
hdc -t <SN> shell "snapshot_display -f /data/local/tmp/shot.jpeg"
hdc -t <SN> file recv /data/local/tmp/shot.jpeg "D:\meida\.scratch\shot.jpeg"
截图按轮次留档(shots-rN/序号-场景.jpeg),既是证据也供事后分析(R4 测试挂掉后,58 张截图让接任 agent 零重复完成判定)。
2.4 观察通道二:UI 树(精确定位靠它)
hdc -t <SN> shell "uitest dumpLayout" # 不带 --p,输出到 /data/local/tmp/layout_<id>.json
# 输出文件名在 stdout:"DumpLayout saved to:/data/local/tmp/layout_85596362629.json"
hdc -t <SN> file recv /data/local/tmp/layout_<id>.json 本地.json
注意:这台 ROM 上 uiautomator 不存在,用 uitest dumpLayout;--p 指定路径在该 ROM 上不生效(recv 报 no such file),让它默认落盘再按 stdout 报的文件名取。
解析(Git Bash 下用 node,比 python 省事——Windows 可能没装 python):
node -e "
const d=JSON.parse(require('fs').readFileSync('本地.json','utf8'));
function walk(n,out){ if(n&&typeof n==='object'){ if(n.text) out.push(n.text+' | '+(n.bounds||'')); for(const k in n) walk(n[k],out);} else if(Array.isArray(n)) n.forEach(x=>walk(x,out)); return out;}
console.log(walk(d,[]).filter(t=>!t.startsWith(' |')).slice(0,30).join('\n'));
"
bounds 格式 [x1,y1][x2,y2],点击坐标取中心 (x1+x2)/2, (y1+y2)/2。系统页面(相机、选择器)的节点可能整块渲染(照片网格只有一个大 Image 节点),此时只能靠截图估坐标。
2.5 观察通道三:hilog(逻辑时序的事实来源)
hdc -t <SN> shell "hilog -r" # 清缓冲(测前清一次,判定干净)
hdc -t <SN> shell "hilog -x | grep MEIDA_IMG | tail -5" # 过滤本项目打点
App 侧要有打点体系才有这一通道。本项目 ImageCodec.ets 里 MEIDA_IMG 域 11 处打点(select 前后/URI 数量/解码/打包/各 stage 失败/超时放弃),照片链 P0 的定位与修复验证全靠它:
gallery: select begin
gallery: select end, 1 uri(s), first=file://media/Photo/… ← 送达
gallery select not settled in 60000 ms, give up ← 超时兜底触发
camera: pick end, resultUri length=63 ← 拍照成功
jpeg: info 4000 x 3000 (120 ms) → pixelMap decoded → packed 284531 bytes ← 压缩链通
新功能开发时先埋打点再上真机,是这套闭环能自动判定的前提。
2.6 崩溃监控
hdc -t <SN> shell "hilog | grep -iE 'com.xiangsilian.app.*(crash|FATAL|JS)'"
3. 视觉判定子环(AI 读图)与防幻觉纪律
截图拿回来后的判定链:
Read截图 → 得到 CDN URLanalyze_image(URL, "具体问题")→ 视觉模型描述(问坐标、问状态、问按钮位置)- 交叉验证:涉及中文文案/精确状态时,以 UI 树 dump 的
text字段为准——实测视觉模型会把「加载中」读成「错误」、把没勾选说成已勾选(R4/R5 各有一次教训)
提问要具体("第二列第一行的照片有没有被勾选?左上角有没有圆圈对勾?"),不要开放式("这页面有什么问题?")。
4. 已固化的坐标库(本机实测,换机/换分辨率需重测)
屏幕 1152×2520。坐标会随版本漂移,用前先 dumpLayout 校验。
| 场景 | 元素 | 坐标(中心) |
|---|---|---|
| 首页 | 试衣间卡片 | (440, 850) |
| 首页 | 报告 Tab / 我的 Tab | dumpLayout 取 |
| 试衣间 | 全身照·相册 / 拍照 | (635, 1176) / (730, 1176) |
| 试衣间 | 上衣·相册 / 拍照 | (382, 1930) / (477, 1930) |
| 试衣间 | 开始试穿 | (576, 2306) |
| 同意弹窗 | 不同意 / 同意并上传 | (350, 1622) / (802, 1622) |
| 系统相机 | 快门 | (576, 2340) |
| 系统相机 | 拍后确认 √ / 取消 ✕ | (1045, 2345) / (100, 2345);不同布局下 √ ≈ (1038, 2360) |
| 照片选择器 | 网格 3 列,列中心 x | 192 / 576 / 960 |
| 照片选择器 | 第一行 y | 975(有安全横幅时;横幅占位会推移) |
| 照片选择器 | 关闭 ✕ | (810, 212) |
| 保存对话框 | 确认键(右上 ✓ 图标) | dumpLayout 取(R3 已注记) |
5. 踩坑全记录(每条都真实花过时间)
5.1 Git Bash 路径转义(最高频)
现象:hdc file recv /data/local/tmp/x.jpeg 报 Error opening file: no such file, path: C:/Program Files/Git/data/local/tmp/x.jpeg——MSYS 把 /data/... 当 Windows 路径转换了。引号包不住。
解法:会话开头 export MSYS_NO_PATHCONV=1。
5.2 install -r 不杀进程
见 §2.1。症状:新修复"无效"、行为与旧版一致。ps -ef 看进程启动时间戳即可识破。
5.3 系统选择器的"安全访问"模式(当前最大边界)
三个事实:
- 合成点击可能对选择器完全无效:照片既不勾选也不进预览(截图前后对比+UI 树无变化+hilog 无 select end 三方证实)。同为安全模式的系统保存对话框则可以点。疑似与 ROM 的反自动化保护或残留状态有关。
- picker 空结果收场后可能残留不可见蒙层,吞掉后续对选择器的一切注入;
aa force-stop com.huawei.hmos.photos可恢复(R5N3 记录,本次复现并验证了恢复手段)。残留的filePicker进程(kill -9报 Operation not permitted)杀不掉,但它自 R4 测试成功期就存在,不是阻断因素。 - 结论:凡关键路径经过"安全访问选择器"的,闭环会在这里断,最后一步交人工(本次 R5 热修的预览刷新/压缩链就停在这一点,已在一分钟验收清单)。绕行思路(未验证):入口改用
photoAccessHelper.PhotoAccessHelper授权+应用内自绘选择器,或用相机路径(A3 可自动化)覆盖测试场景。
5.4 相机自动化的模式面板
点快门附近区域可能拉出"拍照模式"抽屉挡住操作,且抽屉上的点击会切模式。解法:keyEvent Back 收起,重新对准坐标点快门。相机路径里"拍完→√确认"的坐标在同机不同布局下有两版(§4),点完必须截图确认真的出了相机。
5.5 PIN 锁与熄屏
熄屏打断的不只是测试——还打断了正在跑的 agent(R5 开发 agent 就是被用户熄屏操作打断成半成品)。长测试期间请保持亮屏解锁(开发者选项可设"充电时不锁定")。
5.6 hilog 缓冲冲刷
相机/图库会产生海量日志,把 MEIDA_IMG 冲掉。重要判定先 hilog -r 清缓冲再操作,操作完立即取;超时兜底类日志(60s 后才打)要等足时间再取。
5.7 agent 侧的坑
- 上下文超限:R4 测试 agent 塞了 58 张截图+几十个 dump 后挂掉(Invalid string length)。纪律:单 agent 分析截图 ≤20 张、dump 只 grep 不整读、每完成一个场景就落盘中间结论。
- 视觉幻觉:见 §3,一律与 UI 树交叉验证。
- hdc 命令语法差异:
longPress不存在(是longClick);uiautomator不存在(是uitest dumpLayout)——打错会打印 usage,照着改即可。
6. 红线与成本控制(写给下一轮测试 agent)
- 换装/发型真实生成各 ≤1 次(Phase A 直连自有线上服务,虽不走 DeepSeek 计费但要省服务资源);失败不重试烧服务,记录跳过原因
- 对话 ≤2 条(agent_chat 走 DeepSeek 真实计费,单条成本极低但有成本)
- 绝不跑
make smoke(真实 DeepSeek 全链路,仓库 README 标注"唯一花钱的入口") - 后端集成测试(tests/integration)默认跳过,不强行开启(需要 PG/Redis)
- 测试基建(
.scratch/multiagent/checks/checks.mjs)可修,产品代码不可改;发现产品 bug 报告给下一轮,不越权修
7. 一轮标准测试的完整脚本骨架(可抄)
SN=88Z9K26525009283
export PATH="$PATH:/c/Program Files/Huawei/DevEco Studio/sdk/default/openharmony/toolchains"
export MSYS_NO_PATHCONV=1
H() { hdc -t $SN shell "$@"; }
# 0) 门禁
(cd /d/meida/app && cmd //c .build-gate.bat | tail -3) # 期待 BUILD SUCCESSFUL
wsl -e bash -lc "cd /mnt/d/meida/meida_backend && uv run --extra dev pytest tests/unit --no-cov -q | tail -2"
node /d/meida/.scratch/multiagent/checks/checks.mjs | tail -2 # 期待 28/28 PASS
# 1) 装新包并重启进程
hdc -t $SN install -r "D:\meida\app\entry\build\default\outputs\default\entry-default-signed.hap"
H "aa force-stop com.xiangsilian.app"; sleep 1
H "aa start -a EntryAbility -b com.xiangsilian.app"; sleep 4
# 2) 逐场景:清日志 → 注入 → 观察
H "hilog -r"
H "uitest uiInput click 440 850"; sleep 3 # 进试衣间
H "snapshot_display -f /data/local/tmp/s.jpeg"
hdc -t $SN file recv /data/local/tmp/s.jpeg ".scratch/scene.jpeg"
H "uitest dumpLayout" # stdout 给文件名 → recv → node 解析定位
H "hilog -x | grep MEIDA_IMG | tail -5"
# 3) 判定交叉验证 + 留档,进入下一场景……
8. 当前状态与待办
- 已通:装包/重启/启动、页面导航与所有 App 自有 UI 交互、截图/dump/hilog 三通道、PDF 全链(含系统保存对话框)、飞行模式、杀 App 回看、登出重登、同意弹窗、断网错误态
- 半通:系统相机(可自动拍+确认,偶发模式面板干扰)
- 不通:照片选择器勾选(§5.3)——最后一公里人工,或改应用内自绘选择器
- 改进方向:把 §7 骨架固化成
node脚本(场景→步骤→断言三段式),配MEIDA_IMG式打点规范推广到报告域/对话域,即可从"agent 手操"升级为"一条命令跑回归"