Files
meida/docs/真机闭环自动化测试-实践指南.md
xsl c9d13697a6 docs: 五轮多 Agent 评审迭代复盘 + 真机闭环自动化测试实践指南
- 复盘:编排设计、逐轮决策链、35 项修复全量清单、关键技术点、踩坑与复用流程
- 真机指南:闭环模型(注入/截图/dumpLayout/hilog 三通道)、命令手册、坐标库、
  安全访问选择器等 7 条踩坑、成本红线、标准测试脚本骨架
2026-08-30 23:22:02 +08:00

291 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 真机闭环自动化测试 · 实践指南
> 日期: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 工具链路径
```bash
# 每个新 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 设备
```bash
hdc list targets # 应输出序列号,如 88Z9K26525009283
```
**设备必须解锁亮屏**。锁屏(PIN)会挡住一切交互注入——本次迭代 R1~R3 三轮测试全被 PIN 锁阻塞,只完成了"装包+启动+hilog 无崩溃"这层。屏幕熄灭后可用:
```bash
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):
```bash
# 拉起(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`。
单测(不连库不发网络,安全):
```bash
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 装包与启停(★ 有一个致命细节)
```bash
# 构建并安装
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 输入注入
```bash
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 观察通道一:截图
```bash
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 树(精确定位靠它)
```bash
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):
```bash
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(逻辑时序的事实来源)
```bash
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 崩溃监控
```bash
hdc -t <SN> shell "hilog | grep -iE 'com.xiangsilian.app.*(crash|FATAL|JS)'"
```
---
## 3. 视觉判定子环(AI 读图)与防幻觉纪律
截图拿回来后的判定链:
1. `Read` 截图 → 得到 CDN URL
2. `analyze_image(URL, "具体问题")` → 视觉模型描述(问坐标、问状态、问按钮位置)
3. **交叉验证**:涉及中文文案/精确状态时,以 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 系统选择器的"安全访问"模式(当前最大边界)
三个事实:
1. **合成点击可能对选择器完全无效**:照片既不勾选也不进预览(截图前后对比+UI 树无变化+hilog 无 select end 三方证实)。同为安全模式的系统保存对话框则可以点。疑似与 ROM 的反自动化保护或残留状态有关。
2. **picker 空结果收场后可能残留不可见蒙层**,吞掉后续对选择器的一切注入;`aa force-stop com.huawei.hmos.photos` 可恢复(R5N3 记录,本次复现并验证了恢复手段)。残留的 `filePicker` 进程(`kill -9` 报 Operation not permitted)杀不掉,但它自 R4 测试成功期就存在,不是阻断因素。
3. 结论:**凡关键路径经过"安全访问选择器"的,闭环会在这里断**,最后一步交人工(本次 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. 一轮标准测试的完整脚本骨架(可抄)
```bash
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 手操"升级为"一条命令跑回归"