一次 ApiDemos APP 测试的计划评审、环境基线与安装准入

设备一度显示 offline,Appium 的端口没有监听,准备覆盖安装时 ADB 又突然找不到手机。
看起来像连续出了三次事故。
但在这篇文章结束前,我依然不能说 App 有 Bug。原因很简单:这三次异常都发生在真正的 UI 功能测试之前。
这次测试的是 Android ApiDemos 6.0.15。以前我总觉得 APP 测试从打开页面、点击按钮、检查结果开始。真正走完前三步才发现,页面还没点,测试已经开始了。
前三步要解决的不是“功能对不对”,而是三个更基础的问题:
测的到底是不是约定的那个包?
设备和工具链是否真的可用?
这个 APK 是否具备进入 UI 测试的安装条件?
只有这三个答案可信,后面的功能结果才值得相信。
第一步:计划评审不是写文档,而是关闭关键决策
第一步没有连接设备,也没有运行 Appium,只做计划评审与决策关闭。
听起来很“虚”,实际上它决定了后面每个结果能不能被解释。
先固定被测对象
候选 APK 大小为 6,521,237 字节,SHA-256 是:
3D01868BDF3E2248EF94D30A49F81BE9B8140C9A6F398C4E22B2CF109101ABA5
SHA-256 可以理解为文件的指纹。文件只要发生变化,指纹就会跟着变化。
为什么不能只写一句“测试最新 APK”?因为“最新”会变。今天测试的文件和明天交付的文件如果不是同一个,哪怕所有用例都通过,也无法证明交付包通过了测试。
本轮还记录了源工程版本和已知构建差异,但 APK 与源提交的可重现绑定没有独立验证。因此,后续结论始终以这一个 APK 指纹为准,没有把未验证的源码绑定写成事实。
再固定设备和范围
本轮临时范围只有一台设备:
HONOR ADT-AN00;
Android 14 / API 34;
1080 x 2388;
480 dpi。
这意味着后面的测试结果只能代表这台设备和这个系统版本,不能直接扩写成“Android 兼容性通过”。
功能范围也需要提前冻结。哪些顶层入口属于 P0,哪些子菜单不在本轮范围内,都必须在运行前写清楚。否则测试结束后再挑选结果,很容易把没测的内容悄悄算进通过范围。
最后确认授权边界
本轮明确不执行:
卸载应用;
清除应用数据;
重置权限。
这些操作会改变设备状态,而且部分结果难以恢复。测试人员不能因为“这样更方便构造场景”,就默认拥有修改真实设备状态的权限。

第一步最终关闭了四类决策:APK 来源和责任人、设备范围、功能范围、破坏性操作边界。
到这里我学到的第一件事是:测试计划不是待办清单,而是一份证据合同。它规定测什么、怎么判定,以及哪些话不能说。
第二步:环境基线不是准备工作,它本身就是测试
第二步开始核对 APK、设备和工具链,但仍然没有创建 Appium UI 会话。
先证明手机里的包就是候选包
设备已经安装 io.appium.android.apis。从设备取回 base.apk 的 SHA-256 后,它与本地候选 APK 完全一致。
同时确认了:
versionCode=41;versionName=6.0.15;minSdk=26;targetSdk=33;Launcher Activity 为
io.appium.android.apis/.ApiDemos。
这一步解决的是身份问题:测试设备上运行的,确实是第一步批准的候选构建。
设备在线,不是看见序列号就够了
初始检查时,目标设备显示为 offline。
这说明 ADB 知道有这样一台设备,却无法正常与它通信。执行针对目标 UDID 的重连后,设备短暂从列表消失,5 秒后恢复为 device。
offline 和 device 只有一个单词的区别,但对自动化测试来说是两种完全不同的状态。前者不能作为执行前提,后者才表示 ADB 通道可用。
恢复后核对到设备型号、Android 版本、分辨率、密度和锁屏状态,设备基线才算完整。
服务启动了,不等于服务已经就绪
Appium 初始端口没有监听。启动服务后,UiAutomator2 冷加载耗时 28.595 秒,外层命令先等待超时,但服务随后成功监听,健康接口返回:
ready=true
如果只保留“命令超时”这一行,很容易把它判断成环境失败;如果只看最终健康状态,又会漏掉启动等待口径偏短的问题。
正确的记录方式是同时保留过程和恢复结果:外层等待曾超时,这是执行事件;服务最终健康,这是准入证据。
本机工具链也被逐项确认,依赖锁定检查显示无需变更。pytest 最终收集到 4 条基线测试,耗时 0.05 秒。
注意,这里只是证明“配置可以加载、用例可以被发现”,并没有证明 4 条 UI 用例可以通过。
第二步让我意识到:环境问题最危险的地方,不是它会让测试失败,而是它会伪装成脚本问题或产品问题。
第三步:安装成功,只能证明安装准入通过
第三步终于开始检查 APK 是否能进入安装和启动阶段。
本轮允许同版本覆盖安装,但仍然不允许卸载、清除和权限重置。
安装前先检查包,而不是直接执行命令
安装前依次核对:
Manifest 中的包名、版本、SDK 和启动 Activity;
APK 签名完整性;
设备 API 与
minSdk的关系;APK 是否包含原生库及设备 ABI 是否匹配;
/data分区可用空间。
apksigner verify 返回成功,APK Signature Scheme v2 验证通过。签名者使用 Android Debug 证书,它与当前批准的测试候选一致,但不能代表正式发布签名。
设备 API 34 满足 minSdk=26。APK 没有 /lib/ 原生库,因此本轮没有发现原生 ABI 阻断。可用存储也远大于 6,521,237 字节的 APK 大小。
这些检查像登机前确认机票、证件和行李。直接执行安装命令,也许能成功;但失败时,你很难立即知道是签名、SDK、ABI、空间,还是设备连接出了问题。
第一次安装还没开始,设备先消失了
准备覆盖安装时,设备从 ADB 列表消失,安装前状态读取失败。
关键点是:安装命令尚未执行,应用也没有被修改。
所以这次事件应该归类为 USB / ADB 环境波动,而不是“安装失败”,更不是产品缺陷。
设备恢复后连续 3 次返回 device,随后重新执行:
adb install -r <candidate-apk>
命令在 834 毫秒后返回 Success。
覆盖安装后,还要确认没有换包
安装成功提示只是第一层证据。覆盖安装后再次核对:
设备端 APK 指纹仍与候选一致;
版本保持
41 / 6.0.15;数据目录未变化;
首次安装时间未变化;
25 条权限状态摘要保持一致。
这里没有检查应用私有数据的具体内容,所以“私有数据完整保持”仍是 not verified,不能从目录和摘要没变直接推导出来。
随后使用 am start -W -S 冷启动应用,返回 Status: ok,启动 Activity 为 .ApiDemos,总耗时 730 毫秒。对启动后目标进程日志采样 300 行,没有匹配到 FATAL EXCEPTION、ANR 或进程死亡。
这个结论也有严格边界:它只覆盖这一次冷启动和这 300 行采样窗口,不等于应用经过长时间运行仍然稳定。
为什么安装准入通过,安装专项却没有全通过?
因为本轮只执行了被授权的非破坏性范围。
以下项目仍然没有被验证:
全新安装:
blocked,因为不允许卸载或清数;历史版本升级和卸载重装:
blocked,因为没有批准的历史 APK,也不允许卸载;损坏包、签名不一致、空间不足、安装中断:
not verified;多设备安装:
not verified。
因此,准确结论是:当前候选 APK 在这台设备上的同版本覆盖安装准入通过,可以进入下一阶段。
而不是:安装测试全部通过。
这两个说法只差几个字,覆盖范围却完全不同。
三步走完后,我重新理解了 APP 测试
前三步结束时,我还没有点击一个业务页面,却已经完成了三件非常重要的事:
用 APK 指纹、设备范围和授权边界固定了测试对象;
用 ADB、Appium 健康状态和依赖检查证明环境可执行;
用包信息、签名、SDK、ABI、空间、覆盖安装和冷启动证明候选包具备进入 UI 测试的条件。
期间出现的设备离线、服务等待超时和 ADB 瞬时消失,都被保留了下来,但没有被错误地包装成产品缺陷。
这就是我在前三步学到的核心方法:
先确认测试是否有资格开始,再讨论 App 的功能是否正确。
页面上的一次点击很直观,计划、环境和包准入却决定了这次点击的结果能不能被相信。
真正的 APP 测试,不是从点击开始的。
夜雨聆风