
折腾 Android 自动化测试这些年我电脑上装过的adb版本少说也有七八个从最早手动配环境变量配到怀疑人生到后来一条命令跑通整条用例链路。adb 自动化测试这个方向说它门槛高吧其实核心工具就一个命令行程序说它门槛低吧新手卡在驱动、授权、坐标漂移上三天跑不出第一条用例也是常事。这篇文章我打算把从装工具到写出可复用脚本的完整路径讲清楚包括每一步为什么这么做、参数怎么算、哪些坑我亲自踩过。适合刚接触移动端测试的同学、想从手工点点点转向脚本化的测试工程师也适合做设备管理、批量操作类工具的开发同学参考。全程不依赖图形化工具纯命令行加 Python装完就能用。1. ADB 到底是个什么东西为什么值得先学它1.1 从一条命令看 ADB 的角色定位很多人第一次听到 ADB以为它是个测试框架其实不是。ADB 全称 Android Debug Bridge翻译过来叫调试桥本质是一个客户端-服务端-守护进程的三段式通信工具。你在电脑命令行里敲的adb是客户端电脑后台会常驻一个 server 进程默认监听本地 5037 端口而手机或模拟器里跑着一个叫 adbd 的守护进程。三者一串联你在电脑上敲的每条命令最终都会被翻译成指令送到设备上执行执行结果再原路返回。这个结构解释了一个新手最常困惑的现象为什么adb devices有时候明明插着线却显示不出设备因为这条命令走的是客户端请求 serverserver 去扫描已连接的设备这条链路任何一环断了都看不到设备。我一般排查时会把三段分开看——server 有没有起来adb start-server、设备端的 adbd 有没有被授权、USB 通道有没有被系统识别。理解了这个模型后面九成的连接类问题你都能自己定位不用到处搜。再说一个容易被忽略的点ADB 的能力边界其实非常大。它不只是传个文件、装个应用还能模拟点击滑动、抓取界面结构、读取系统性能数据、修改部分系统设置。这意味着你完全可以在不写一行 Android 代码的前提下用 shell 命令拼出一套可用的界面自动化流程。这就是为什么很多轻量级自动化方案底层都是直接调 ADB而不是上来就搭一整套重型框架。1.2 ADB 自动化测试能解决什么问题不能解决什么先把能解决的说清楚。ADB 自动化最典型的场景有三类批量设备操作几十台设备同时装包、清数据、跑冒烟用例、稳定性压测循环点击、随机滑动配合日志抓取看崩溃、数据采集与校验截图、dump 界面、拉性能数据做对比。这几种场景共同的特点是动作相对固定、对界面识别精度要求没那么高、更看重能跑起来且好维护。不能解决的也说清楚免得你走弯路。ADB 原生命令做自动化最难受的是元素定位。input tap只能按坐标点而坐标在不同分辨率、不同刘海屏、不同系统导航方式下都不一样换台机器就全废。所以纯 ADB 方案通常要配合固定分辨率或者动态换算代码量会上来。另外 ADB 拿不到应用的内部状态你没法直接问这个页面加载成功了吗只能靠界面元素、日志关键字、截图比对来间接判断。所以我的建议是如果你只是想做设备侧的批量操作、日志抓取、稳定性压测ADB 足够了别上重型框架如果你要做带断言、带数据驱动、要跨机型稳定运行的业务测试那把 ADB 当作底层能力上面再套一层能识别元素的框架会更省心。这个判断很重要我见过太多人为了一个每天跑两次的安装脚本硬上整套 Appium最后维护成本比收益还高。2. 环境搭建从零到能跑通第一条命令2.1 工具包选择与安装路径规划装 ADB 最省事的方式是直接拿官方 platform-tools 包它里面就包含 adb、fastboot 这几个可执行文件解压即用不依赖 Android Studio。我不建议新手为了一个 adb 去装几个 G 的完整 IDE除非你后面确实要用它的模拟器。另外网上流传的一些一键安装脚本也能用本质就是把 platform-tools 下载下来并自动写环境变量图省事可以选但要知道它干了什么不然出问题你没法排查。安装路径我有个个人习惯不要放在带空格或中文的目录下比如C:\Program Files\或者桌面上的中文文件夹。原因很实际很多脚本在调用 adb 时是拼接字符串的路径里有空格就会把参数切断报一个莫名其妙的系统找不到指定文件。我一般固定放在D:\tools\platform-tools\这种纯英文短路径下。macOS 用户直接用包管理器装更舒服Linux 下同理装完都是一条命令验证。配置环境变量的关键点是把 platform-tools 目录加进 PATH。Windows 上改完一定要新开一个命令行窗口再验证老窗口读的还是旧环境变量这是新手最容易怀疑人生的地方。验证命令很简单敲adb version能打出客户端版本号和安装路径就说明通了。如果提示不是内部或外部命令就是 PATH 没生效或者路径写错了回去重新检查。2.2 驱动、授权与设备连接方式工具装好了接下来是让电脑认识手机。Windows 上这一步最容易卡因为不同厂商的 USB 驱动不通用。现在的做法一般是先在手机端开启开发者选项——进设置找到关于手机连续点击版本号七次会提示你已成为开发者。然后回到设置里进开发者选项打开USB 调试。部分机型还有USB 安装USB 调试安全设置这类开关涉及模拟点击的自动化建议一并打开否则input类命令会被系统拦掉。第一次插线连接时手机会弹一个是否允许 USB 调试的授权框。这里有个细节勾选一律允许再确认否则每次重插都要点一遍。如果你不小心点了拒绝或者换了电脑设备状态会显示 unauthorized。解决办法是在开发者选项里点撤销 USB 调试授权拔插一次重新授权。还不行的话就是电脑侧的密钥文件坏了Windows 在用户目录的.android文件夹下把adbkey和adbkey.pub删掉然后adb kill-server再adb start-server会重新生成一对密钥再走一次授权。无线连接现在也很好用了。Android 11 及以上支持官方配对流程先在开发者选项里打开无线调试选择使用配对码配对设备它会给出一个 IP 端口和六位配对码电脑上执行配对命令再执行连接命令就能连上。老版本系统就只能先用 USB 连一次把设备切到 TCP 模式拔线后再用 IP 连。无线的好处是自动化时不用管线材接触不良坏处是网络抖动会影响命令稳定性做长时间压测我还是建议插线。2.3 环境自检清单与常见报错对照环境搭完别急着写脚本先跑一遍自检。我的习惯是按顺序敲四条命令adb version看工具、adb devices -l看设备列表带 -l 能多看到型号和传输方式、adb shell getprop ro.product.model看能不能真正进到设备里、adb shell input keyevent 3看能不能把设备按回桌面。这四步都通过说明整条链路是活的后面出问题大概率是脚本逻辑而不是环境。下面这张表是我这些年攒下来的高频报错对照建议直接存下来比搜帖子快得多现象大概率原因处理动作devices 列表为空线材/接口/驱动/server 挂了换线换口重启 server检查设备管理器显示 unauthorized未授权或密钥不匹配撤销授权重连删 adbkey 重生成显示 offlineadbd 卡死或系统休眠重插设备必要时重启设备端 adbd提示 more than one device多设备同时在线用 -s 参数指定设备序列号input 命令无反应未开启调试安全设置打开对应开关或改用界面自动化方案中文输入乱码input text 不支持非 ASCII改用输入法广播或粘贴方式这里展开说两个最容易踩的。一个是多设备场景-s参数是你必须养成的习惯任何时候只要有超过一台设备在线所有命令都建议带上它不然脚本随机打到哪台设备上你都不知道压测时特别容易出事故。另一个是input text的字符限制它只认 ASCII 字符空格要写成%s中文直接失败。这个限制很硬绕不过去实际项目里我一般用两种方案要么把中文内容通过剪贴板注入再用粘贴键触发要么干脆在应用里预留测试账号避开中文输入。3. ADB 常用命令的实操地图3.1 设备与应用管理类命令这一类命令是你每天都要用的基本盘。看设备、看包名、装包卸包、启停应用构成了自动化脚本的骨架。装包这块参数值得记一下-r表示覆盖安装保留数据-t允许安装测试包-d允许降级安装。这三个参数在 CI 流水线里几乎必带因为构建产物经常是同版本号重复安装或者带测试标记少一个参数就装不上报错信息还特别含糊。启停应用是自动化里用得最频繁的一对动作。启动用am start可以指定包名和 Activity也可以用打开链接的方式唤起很多应用的深链就是这么调起来的停止用am force-stop注意它是强杀会清掉进程但不清数据。我见过有人想清数据却用 force-stop结果每次跑用例都带着上次的缓存状态问题排查了半天。要清数据得用pm clear这个才是真正把应用数据目录抹掉。还有一组查信息的命令很实用pm list packages配合筛选能找到目标包名dumpsys package能看版本号和权限getprop能读各种系统属性比如系统版本、机型、CPU 架构。这些信息在自动化脚本里经常用来做分支判断比如只在 Android 12 以上走这条流程或者在报告里记录设备指纹方便复现问题时知道是在什么机器上挂的。3.2 输入事件与界面交互类命令模拟交互的核心就三个命令input tap点击、input swipe滑动、input keyevent按键。点击传的是屏幕绝对坐标滑动传起点终点加时长时长参数非常关键——给太短会被识别成快速滑给太长又变成慢拖一般来说三百到五百毫秒是比较接近人手操作的区间。我做列表滚动的时候会反复试这个值因为不同应用的滚动惯性不同参数没调好就会滑过头或者滑不动。按键这块键码不需要全背常用的记住几个就够了3 是返回桌面4 是返回上一级26 是电源66 是回车67 是删除82 是菜单。另外input keyevent支持长按和组合比如截屏这种组合键用起来比自己拼手势可靠。还有个冷门但好用的input text我们已经说过它的限制补充一句连续输入多个单词用%s代替空格特殊符号要转义写脚本时拼字符串要小心 shell 的解析层我一般会在 Python 里用参数列表的方式传避免被 shell 二次解析。比坐标点击更稳的做法是配合界面结构来定位。ADB 有个uiautomator dump命令可以把当前界面所有控件树导出成 XML里面有每个元素的文本、资源 ID、坐标范围。你可以先 dump 一次找到目标元素再取它的中心点坐标去点击。这比硬编码坐标稳太多了换机型也能通过重新 dump 自动适配。代价是多一步操作、多一点耗时但对稳定性要求高的场景这个代价值得。3.3 日志抓取与性能数据采集日志是自动化测试的眼睛尤其做稳定性测试的时候界面崩了你可能都没注意但日志里一定有痕迹。adb logcat的用法我建议按场景分日常排查用带时间戳的格式实时看过滤某个标签用标签筛选只想看错误就设优先级为 error排查崩溃专门看 crash 缓冲区需要出报告就把日志导出成文件。有一点必须提醒开始跑用例前先清一次日志缓冲否则你导出来的日志里混着一堆历史记录定位问题时要翻很久。性能数据采集是另一个高频需求。内存用dumpsys meminfo指定包名能拿到 PSS 这类比较接近真实占用量的指标CPU 用dumpsys cpuinfo或者top抓瞬时快照电量用dumpsys battery界面流畅度用gfxinfo配合帧统计参数能拿到每一帧的渲染耗时。这些命令单看一次意义不大真正有用的是定时采样然后看趋势比如每十秒采一次内存跑两小时看曲线是平的还是稳步上升后者基本就是内存泄漏。采样频率这个事情值得单独说。采太密了命令本身就会干扰被测应用采太疏了又看不出瞬时尖峰。我的经验是内存和 CPU 五到十秒一次比较合适流畅度这种涉及单帧的要连续采集。另外所有采样命令执行本身都有一点耗时脚本里别把它们放在对时序敏感的步骤中间会打乱原本的操作节奏。3.4 文件传输、截图与录屏文件传输就两条命令推文件进设备、拉文件出设备。看着简单但有三个坑路径权限、文件大小、传输中断。往系统目录推文件需要 root 或者推到应用私有目录需要相应权限一般测试场景建议推到公共目录下再处理。大文件传输要有超时机制脚本里不设超时遇到传输卡住会一直挂着。还有就是传输完成不等于落盘完成紧接着读取可能会拿到不完整文件稳妥做法是传输后校验文件大小。截图我强烈建议用一条管道命令直接输出到电脑而不是先存到设备再拉出来。后者的坏处是慢而且部分系统在传输过程中会做换行符转换把 PNG 文件搞坏打开一看全是雪花。管道方式一条命令搞定速度快很多适合高频截图或者做逐帧比对。录屏用 screenrecord支持指定时长上限、码率和分辨率。实测分辨率降一档、码率降一点文件体积能小很多对排查问题完全够用因为你要看的是操作流程和报错弹窗不是电影画质。需要注意的是录屏在部分设备上有时长上限长流程要分段录或者用别的方案。录屏和截图都挺吃性能做性能测试时不要同时开着数据会严重失真这个细节我在早期项目里吃过亏测出来的卡顿根本分不清是应用的还是录屏本身的。4. 从手动敲命令到脚本化ADB 自动化测试的落地写法4.1 Python 封装 ADB 的通用骨架手动敲命令只能验证思路真要跑起来必须脚本化。Python 是最顺手的选择标准库就够了不需要装额外依赖。核心就一个执行函数接收命令参数列表调用子进程执行捕获标准输出、标准错误和返回码加上超时保护。这里我强调用参数列表而不是拼一整个字符串一来避免路径空格被切断二来避免特殊字符被 shell 解析安全性也好得多。封装的时候有三件事必须做。第一统一加超时任何一条 adb 命令都可能因为设备掉线永远不返回没有超时脚本就会挂死。第二统一记录每条命令的执行耗时和输出出问题时有据可查我一般会打到一个日志文件里跑完直接翻。第三把返回码当回事命令执行失败要抛异常而不是静默继续否则你会看到脚本顺利跑完但实际什么都没做成。import subprocess import logging def adb(args, serialNone, timeout30): cmd [adb] if serial: cmd [-s, serial] cmd args logging.info(RUN %s, .join(cmd)) result subprocess.run( cmd, capture_outputTrue, textTrue, timeouttimeout, ) logging.info(RC%s OUT%s ERR%s, result.returncode, result.stdout.strip()[:200], result.stderr.strip()[:200]) if result.returncode ! 0: raise RuntimeError(fadb failed: {result.stderr.strip()}) return result.stdout这段代码在实际项目里可以直接用我用了很多年只做过很小的调整。你可能注意到我把输出截断了因为有些命令比如 dump 整个界面输出能有几万行全打进日志会把日志文件撑爆。截断一部分用于排查足够了需要完整输出的场景单独处理。4.2 一个可复用的业务流程示例光有底层函数不够得往上搭一层业务动作。我习惯按设备操作和业务操作分两层底层是 install、launch、tap、swipe 这种通用动作上层是登录流程下单流程这种业务语义。分层的最大好处是业务逻辑变化时只改上层底层稳定不动换设备时只动配置业务代码不用碰。拿一个典型流程举例装包、清数据、启动、等待首页加载、点击某入口、断言页面出现预期文字。每一步都有细节。等待首页加载不能硬 sleep要做轮询比如每三百毫秒 dump 一次界面看目标元素有没有出现超过三十秒算超时。断言也不能只看截图最好是 dump 界面 XML 然后查找关键字这样失败时能打印出当前界面的所有文本排查起来一目了然。坐标的处理是另一个重点。我会在脚本启动时先读一次屏幕分辨率然后把坐标写成比例值比如点击横向百分之五十、纵向百分之八十的位置运行时再换算成绝对坐标。这样同一套脚本在 1080P 和 2K 屏上都能用。这个技巧看着不起眼但它是我从一套脚本只能跑一台机器进化到一套脚本跑几十台的关键一步。4.3 断言、等待与稳定性设计的三个原则第一个原则所有等待都是条件等待不是时间等待。硬编码 sleep 是脚本不稳定的头号原因。设备快的时候 sleep 浪费时间设备慢的时候 sleep 直接失败。正确做法是轮询加超时上限条件满足立刻继续超时才报错。第二个原则所有断言都要带上下文快照。断言失败的那一刻你只有一次机会把现场留下来。我的做法是断言失败时自动截一张图、dump 一次界面、抓最后两百行日志三样一起存到以时间戳命名的目录里。这样第二天看报告的时候不用重现就能知道当时界面上是什么。第三个原则前置状态必须显式清理。每次用例开始前清数据、杀掉残留进程、把屏幕点亮并解锁。这些动作看着琐碎但缺一个就可能让整条用例在诡异的地方失败。我曾经因为没关动画导致点击后界面过渡还没结束就去 dump拿到的是中间态断言一直失败查了很久才发现是动画的锅。后来我把关闭窗口动画、过渡动画、动画时长缩放这三项写进了脚本的初始化步骤问题再没出现过。5. ADB 自动化与上层框架怎么选5.1 三种常见技术路线的对比市面上主流的移动端自动化大概分三条路纯 ADB 命令、系统自带界面自动化能力、跨平台框架。它们不是互相替代的关系更像是三层可以叠加的能力。纯 ADB 最轻、依赖最少、对设备侵入性最低但元素定位能力最弱系统自带的界面自动化能力元素定位强、执行速度快缺点是通常需要打包一个测试程序进去对被测应用有一定要求跨平台框架生态最全、报告最漂亮代价是环境复杂、启动慢、踩坑点多。维度纯 ADB 命令系统界面自动化跨平台框架环境复杂度低中高元素定位能力弱强强执行速度快快中跨应用能力强一般一般适合场景批量操作、压测、日志采集业务用例、稳定回归多端统一、报告体系这张表是我自己踩过一圈之后总结的重点看适合场景那一行。很多人选型时盯着功能最强这一列看结果选了个最重的方案维护一个季度就没人愿意碰了。选型要看你的真实需求需求是每天给二十台设备装包清数据那就该选最轻的。5.2 选型决策与实际取舍经验我的决策逻辑是这样的先问要不要做元素级断言不要就纯 ADB再问要不要跨应用跳转要就纯 ADB 或者系统方案再问要不要在多平台上跑同一套用例要才考虑跨平台框架。三个问题问完答案基本就出来了。绝大多数团队的真实需求落在第一个问题之后也就是纯 ADB 就能覆盖剩下的精力应该花在脚本稳定性上而不是框架选型上。另外一个经常被忽略的取舍是团队能力。一个只熟悉 Python 的测试同学上手纯 ADB 脚本可能三天就能出活换成跨平台框架光环境问题就够折腾一周还要学它那套元素定位语法和等待机制。选型不是选最先进的技术是选团队能持续维护的技术。我见过太多项目死在框架搭起来了但没人会改上。最后一个建议不要一上来就追求大而全。先把一条最核心的用例用 ADB 脚本跑通跑一个星期看稳定性稳定了再复制第二条、第三条。这个过程中你会自然发现需要抽象什么、需要封装什么比一开始就设计一套完美架构务实得多。我自己的自动化工程基本都是这么长出来的先能用再好用最后才是通用。6. 常见问题与排查技巧实录6.1 连接与授权类问题速查连接类问题的排查顺序我总结成一句话先看 server再看线再看授权最后看设备。大部分时候问题出在前两步。adb kill-server加adb start-server能解决相当比例的玄学问题尤其在你装过多个版本的 adb、或者用过模拟器和真机混插之后。因为不同版本的 server 可能会抢 5037 端口老进程占着新命令连过去就出错。授权类问题我前面讲过处理办法这里补充一个容易忽略的场景模拟器。多数模拟器会自带一份 adb路径可能和你自己装的不一样导致电脑上出现两个 server 互相抢端口。典型表现是adb devices一会儿能看见一会儿看不见。处理办法是统一用一个 adb把模拟器的 adb 路径从环境变量里去掉或者直接用模拟器自带的那个。这个坑我踩过不止一次每次都是排查半天才想起来。还有一种情况是设备能被识别但状态是 offline。这通常是设备端的 adbd 卡住了先拔插一次还不行就重启设备再不行就检查是不是有线材质量或者 USB 口供电的问题。用劣质数据线做自动化是我见过的稳定性问题里最隐蔽的一类表现是长时间跑着跑着就掉线换根线就好了。做长时间压测前先确认线材和接口可靠能省掉大量无意义的排查时间。6.2 命令执行不稳定与坐标漂移的处理坐标漂移是纯 ADB 方案最头疼的问题。同一条点击命令今天能点中明天点不中多半是这几个原因屏幕分辨率变了、系统导航方式换了三键导航和手势导航的可用区域不一样、弹窗挤占了位置、页面滚动位置不同。处理思路是尽量不依赖绝对坐标能通过界面结构拿元素坐标就拿拿不到就用比例换算加区域容错。还有一个隐蔽的原因等待不足导致界面没稳定就点了。表现出来就像坐标漂移实际上是点击时界面还在动画中。判断办法很简单在点击前加一段轮询确认目标元素已经存在且位置连续两次 dump 一致再执行点击。这个连续两次一致的技巧很好用能过滤掉绝大多数动画中间态。重复执行失败的情况也要考虑速率问题。有些应用的点击有防抖或者节流连着快速点很多次后面几次会被忽略。做循环压测时我会在两次操作之间插入一个小的随机间隔既模拟真实用户也避免触发这类限制。这个间隔不要写死用随机范围效果更好。6.3 日志与性能问题的定位技巧日志定位的核心是关键字收敛。一上来就看全量日志是没用的要先确定你要找什么。崩溃看 crash 缓冲区应用自身的异常看它自己的日志标签系统层面的杀掉进程看 activity 管理器的相关记录卡顿看掉帧相关的统计。每一类问题都有对应的入口用错了入口只会看到一堆无关内容。一个非常实用的技巧是在测试流程里打标记。就是在执行关键步骤前往日志里写一条自定义标记这样你导出的日志里就有了清晰的分段哪一段出问题一目了然。写标记的方式可以是通过 shell 命令写一条系统日志简单有效。我之前排查一个偶现问题就是靠这个分段定位到是某个特定操作之后的网络请求超时导致的。性能问题还有个观念要纠正不是所有卡顿都值得优化。做自动化测试时优先关注趋势性恶化和崩溃单次的流畅度波动很容易被误判。我的做法是长期采样然后看基线比如首页加载时间连续十个版本都在两百毫秒左右某天变成八百毫秒这才是有价值的信号。单次采样就下结论往往是在浪费时间优化一个本来就没问题的环节。最后分享一个我自己摸出来的小习惯给每条自动化用例的日志文件命名带上设备序列号和时间戳然后统一归档到一个按日期分层的目录里。跑了几十台设备之后你一定会遇到昨天那台机器为什么挂了这种问题有了这套归档翻起来五秒钟就能找到当时的完整记录。这个习惯本身不产生任何直接价值但它能帮你把每次踩坑都变成可复用的经验长期看是最划算的一笔投入。