移动端自动化:Android 测试为什么值得用 ADB 自动化

一提到 Android 自动化,第一反应就是 Appium、UI 框架、云真机平台,或者直接想到“移动端自动化门槛很高”。但如果从真实项目落地来看,更应该先把 ADB 讲清楚。

因为 ADB 不是一个零散命令集合,而是一套移动端测试最底层、最稳定、最容易被低估的工程能力。它解决的不是“高阶自动化炫技”,而是下面这些高频、反复出现的问题:

  • APK 怎么批量安装和卸载
  • 测试前怎么把设备状态拉齐
  • 崩溃、闪退、无响应到底怎么快速留证
  • 多台设备怎么统一执行基础动作
  • 线上问题、预发问题、真机兼容性问题怎么先拿到第一手证据

之所以移动端测试始终停在“人肉点点点”,并不是因为不会写复杂自动化,而是因为设备控制、日志留存、证据采集、状态重置这些基础能力没先打好。没有这层基础,后面接 Appium、接平台、接批量调度,成本都会被成倍放大。

所以第四章先从 ADB 开始,而不是先讲大而全的移动端自动化框架。

一、为什么 Android 测试值得先做 ADB 自动化,而不是一上来就做 UI 框架

如果把移动端测试工作拆开,你会发现很多高频动作其实根本不需要完整 UI 自动化框架。

比如这些事情:

  • 安装测试包
  • 清理应用数据
  • 启动指定页面
  • 导出日志
  • 截图、录屏
  • 查看设备列表
  • 拉取崩溃信息
  • 模拟点击返回键、Home 键
  • 检查设备是否在线

这些动作大多数都可以直接通过 adb 完成,而且往往比 Appium 更轻、更稳、更容易接入 Jenkins 或你自己的测试平台。

这也是我一直强调的一点:移动端自动化不是只有“页面驱动自动化”这一种形态。

在真实项目里,ADB 至少有四类价值。

1. 它是设备控制层

你不管后面用什么框架,只要对象是 Android 真机或模拟器,最后都绕不开设备连接、安装、状态检查、日志获取这些动作。ADB 是最底层的入口。

2. 它是问题定位层

很多移动端问题第一时间并不需要“重跑 UI case”,而是需要先拿到:

  • logcat
  • 崩溃堆栈
  • 截图
  • 当前页面活动信息
  • 应用进程状态

这些动作如果没有自动化,排查速度会非常慢。

3. 它是证据留存层

移动端问题最怕复现时没有现场。ADB 很适合和脚本结合,自动拉日志、自动截图、自动录屏,把一次失败变成可回看的证据包。

4. 它是平台能力的前置层

如果以后要做设备池、批量执行、巡检、冒烟回归,ADB 几乎都会成为底层能力之一。你可以不直接把它暴露给用户,但平台内部最好有这层抽象。

二、哪些场景最适合先用 ADB,而不是直接上 Appium

下面这些场景通常更适合优先交给 ADB 自动化:

1. 安装、卸载、重装、清缓存

这是最基础但最频繁的动作。只要测试版本切换快、环境切换多、需要反复重置状态,ADB 都比手工稳定。

2. 冒烟前置准备

比如:

  • 拉起应用
  • 登录前清数据
  • 设置测试账号环境
  • 打开开发者菜单
  • 导出设备信息

这些动作很多不值得写成完整 UI 用例,用 ADB 更合适。

3. 失败留痕

比如 case 失败时自动执行:

  • adb shell screencap
  • adb exec-out screencap -p
  • adb shell screenrecord
  • adb logcat -d

这类动作的收益非常高,而且接入成本低。

4. 设备批量巡检

比如每天检查设备池是否在线、剩余空间是否充足、系统版本是否异常、是否有僵尸进程、是否残留旧安装包。这些都更适合 ADB。

5. 非 UI 交互型验证

有些问题本质上不是页面交互,而是:

  • 应用是否成功安装
  • 进程是否存活
  • 崩溃是否出现
  • 日志是否输出关键字
  • 前后台切换后状态是否保留

这类问题用 ADB 的性价比很高。

三、怎么把 ADB 能力做成可复用的测试资产

如果只是本地临时输命令,ADB 当然也能用,但那不叫测试资产。真正有价值的是把它做成可重复执行、可排障、可扩展的基础能力。

更倾向的组织方式是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mobile-tools/
├── adbkit/
│ ├── devices.py # 设备发现与状态校验
│ ├── app.py # 安装、卸载、清数据、启动
│ ├── capture.py # 截图、录屏、文件拉取
│ ├── logcat.py # 日志采集与过滤
│ ├── shell.py # adb shell 封装
│ └── errors.py # 常见错误分类
├── scripts/
│ ├── smoke_prepare.py # 冒烟前置准备
│ ├── collect_evidence.py # 失败证据采集
│ ├── reset_device.py # 设备状态重置
│ └── check_devices.py # 设备池巡检
└── outputs/
├── screenshots/
├── recordings/
└── logs/

这个结构的重点不在“代码优雅”,而在职责清晰:

  • devices 只管设备发现和连通性
  • app 只管应用生命周期动作
  • capture 只管证据产物
  • logcat 只管日志采集和裁剪
  • scripts 面向真实任务编排

这样以后你接 Jenkins 也好,接平台也好,底层都还是这些稳定能力。

四、真实落地时,比命令列表更重要的是哪几类 ADB 任务

如果按真实测试工作来分,比“命令列表”更重要的是下面几种任务模型。

1. 设备连通性检查任务

执行目标:

  • 检查设备是否 device 状态
  • 排除 offlineunauthorized
  • 输出设备序列号、型号、系统版本

典型命令:

1
2
3
4
adb devices -l
adb -s <serial> get-state
adb -s <serial> shell getprop ro.product.model
adb -s <serial> shell getprop ro.build.version.release

2. 应用重置任务

执行目标:

  • 安装 APK
  • 卸载旧包
  • 清理应用数据
  • 拉起指定 Activity

典型命令:

1
2
3
4
adb -s <serial> install -r app.apk
adb -s <serial> uninstall com.example.app
adb -s <serial> shell pm clear com.example.app
adb -s <serial> shell am start -n com.example.app/.MainActivity

3. 失败证据采集任务

执行目标:

  • 导出当前日志
  • 立即截图
  • 必要时录屏
  • 拉取应用目录中的调试文件

典型命令:

1
2
3
4
adb -s <serial> logcat -d > app.log
adb -s <serial> exec-out screencap -p > screen.png
adb -s <serial> shell screenrecord /sdcard/fail.mp4
adb -s <serial> pull /sdcard/fail.mp4 .

4. 设备恢复任务

执行目标:

  • 停止残留进程
  • 回到桌面
  • 亮屏解锁
  • 清掉录屏和临时文件

典型命令:

1
2
3
4
adb -s <serial> shell input keyevent 82
adb -s <serial> shell input keyevent 3
adb -s <serial> shell am force-stop com.example.app
adb -s <serial> shell rm -f /sdcard/fail.mp4

真正有价值的是把这些任务标准化,而不是让每个人临时拼命令。

五、在项目里踩过的几个典型坑

如果只看文档,ADB 好像非常顺手。但一旦进入团队协作和批量执行阶段,坑会非常多。

坑 1:设备显示 online,但实际不可用

现象:

  • adb devices 能看到设备
  • 但安装 APK 卡住
  • 或者 adb shell 超时

第一判断通常会误判成:

  • APK 太大
  • USB 线有问题
  • 设备性能太差

但我后面碰到更多的是:

  • ADB server 状态异常
  • 设备弹了 USB 调试授权,但没人点
  • 设备屏幕锁住且系统弹框挡住了交互

排查方式:

1
2
3
4
5
adb kill-server
adb start-server
adb devices -l
adb -s <serial> get-state
adb -s <serial> shell echo ok

如果 get-state 不稳定、shell echo ok 经常卡住,更稳的做法通常不是继续跑任务,而是直接把设备标记为不可调度。

坑 2:日志太多,真正有效信息被淹没

最常见的第一版写法是:

1
adb logcat > all.log

结果日志巨大、噪声极高,真正的崩溃线索反而很难找。

我后面会改成两种方式:

  1. 先在任务开始前清日志:
1
adb -s <serial> logcat -c
  1. 结束后只导出本次日志:
1
adb -s <serial> logcat -d > current.log

如果目标明确,还会加过滤条件,比如只看某个 tag、某个进程、某类崩溃关键字。

坑 3:多设备并发时串设备

这是最常见、也最容易把人坑惨的问题。

现象:

  • 你以为在操作设备 A
  • 实际命令打到了设备 B
  • 最后日志、截图、安装包全串了

根因往往很简单:没有强制使用 -s <serial>

处理原则非常硬:

  • 任何脚本都必须传设备序列号
  • 底层封装不允许省略 serial
  • 输出目录必须按设备号隔离

例如:

1
2
3
4
5
outputs/
└── 7AHT123456/
├── current.log
├── screen.png
└── device_info.json

只要不做这层隔离,后面证据留存几乎一定会乱。

坑 4:把 ADB 当成完整 UI 自动化替代品

这是另一个常见误区。

ADB 很强,但它不是万能的。

它很适合:

  • 设备控制
  • 日志采集
  • 证据留存
  • 基础交互
  • 稳定性巡检

但如果你要做:

  • 复杂页面断言
  • 跨页面流程编排
  • 组件级稳定定位
  • 高可维护的业务回归套件

那最终还是要上 UI 自动化框架,或者至少和 UI 框架结合。

所以正确定位不是“ADB 替代一切”,而是“ADB 先把最底层的可控性建立起来”。

六、怎么排查一个 Android 自动化失败到底是设备问题、环境问题还是业务问题

这类问题如果没有顺序,维护者很容易直接本地重跑,然后靠经验猜。

更倾向的判断顺序是:

第一步:先判断设备是否可用

看:

  • adb devices -l
  • adb -s <serial> get-state
  • adb -s <serial> shell echo ok

如果这一步都不稳定,后面所有业务结论都不可信。

第二步:确认应用基础动作是否正常

看:

  • 能不能安装
  • 能不能启动
  • 能不能 clear data
  • 能不能强制停止后重启

如果这些动作失败,更像环境问题或包问题,不要先怀疑业务逻辑。

第三步:抓本次任务日志

通常会在失败瞬间留下面这些证据:

  • current.log
  • screen.png
  • device_info.json
  • 必要时加 record.mp4

然后再去判断:

  • 是系统权限问题
  • 是应用闪退
  • 是接口异常导致页面加载失败
  • 还是自动化本身脚本问题

第四步:最后再回到业务逻辑

恰恰反过来,一失败就先说“是不是代码有 bug”。但移动端自动化场景里,设备波动和环境问题的占比经常比直觉里更高。

所以更强调先证据、后判断;先设备、后业务。

七、为什么更关键的是 ADB 是移动端自动化体系里的“地基”,不是配角

如果只把 ADB 当成几条临时命令,它确实不显眼。但如果从长期建设角度看,它几乎决定了移动端测试体系的下限。

因为它直接影响:

  • 设备是否可控
  • 证据是否能自动沉淀
  • 问题是否能快速定位
  • 多设备是否能批量管理
  • 上层平台是否有稳定基础设施

我越来越倾向于把它理解成 Android 测试里的“设备控制和证据采集底座”。

先把这层打稳,后面你无论是接:

  • Appium
  • UIAutomator2
  • 自研真机平台
  • Jenkins 批量任务
  • 巡检系统

都会轻很多。

八、这件事真正带来的收益是什么

如果只从“会不会几条命令”来看,ADB 自动化好像不算什么。但真正做成资产以后,收益通常很具体:

  • 安装、清数据、拉起应用这些重复动作不再手工执行
  • 失败后第一时间就能拿到截图和日志,而不是事后复现
  • 多设备测试不再靠人记设备号和手工切换
  • Jenkins 或平台任务可以直接复用底层设备能力
  • 移动端问题定位从“猜”变成“先看证据再判断”

很多时候,团队不是缺一个更高级的框架,而是缺一个能让现有测试动作稳定运转的基础层。ADB 正好就是这层。

九、ADB 的边界和后续升级方向

最后也要讲清楚边界。

ADB 很值得做,但它不是完整答案。

它不适合单独承担:

  • 复杂业务页面自动化
  • 富交互页面断言
  • 跨端联动的完整流程回归
  • 长链路 UI 可维护脚本体系

所以更合理的升级路线通常是:

  1. 先用 ADB 把设备控制、日志采集、证据留存做起来
  2. 再把高频、稳定、关键页面接入 UI 自动化框架
  3. 最后把设备管理、任务调度、结果沉淀接进 Jenkins 或平台

我自己更认同的节奏不是“一步到位做大平台”,而是先把最底层、最容易高频复用的能力沉淀出来。移动端测试里,ADB 往往就是那个最应该先做的起点。