拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Android自动化测试实战:从adb命令到脚本化设备操作

1. 从手动点击到脚本驱动为什么我们需要adb自动化测试如果你是一名Android开发者或者测试工程师每天花在设备上重复安装、启动、点击、截图、卸载应用的时间加起来可能比你写新功能的时间还长。我经历过这个阶段尤其是在做兼容性测试或者回归测试时面对几十台不同型号、不同系统的设备那种重复劳动的疲惫感非常真实。后来我开始系统性地使用adb命令并基于它编写自动化脚本这彻底改变了我的工作流。它不仅仅是“偷懒”更是将测试过程标准化、可追溯、可复现的关键一步。adb全称Android Debug Bridge是Google官方提供的多功能命令行工具。大多数人用它来装个App、抓个日志这其实只发挥了它10%的功力。它的核心价值在于它提供了一个与Android设备包括真机和模拟器进行深度交互的桥梁。通过一系列命令我们可以精确地模拟用户的几乎所有操作点击、滑动、输入文字、启动Activity、获取界面元素、截图等等。将这些命令按照一定的逻辑组织起来就是一个最直接、最轻量级的自动化测试脚本。这种基于adb的自动化特别适合以下几种场景快速冒烟测试在每日构建后自动安装并打开应用检查主流程是否崩溃兼容性测试批量在不同设备上执行相同的操作序列对比结果压力测试编写循环脚本反复执行某个操作如快速切换页面以及数据准备自动化地向设备中导入测试数据或配置文件。它的优势在于零依赖只需要Android SDK里的adb工具、跨平台Windows/macOS/Linux命令基本一致、学习曲线平缓命令本身很直观。当然它不适合做复杂的UI断言和业务逻辑校验那是Appium、Espresso等更高级框架的领域。但对于大量重复、机械的设备和应用操作任务adb脚本是效率提升的“第一把快刀”。2. 环境基石搭建稳定可靠的adb命令行工作台在开始编写任何脚本之前一个稳定、配置正确的adb环境是重中之重。很多新手遇到的“设备未识别”、“命令无响应”问题十有八九出在环境配置上。2.1 SDK与平台工具的正确安装首先你需要获取Android SDK Platform-Tools这里面包含了adb、fastboot等核心工具。最推荐的方式是直接去Android开发者官网下载独立的Platform-Tools包而不是安装完整的Android Studio。这样做的好处是环境纯净避免IDE带来的复杂配置干扰。下载解压后你需要将工具的路径添加到系统的环境变量PATH中。这是关键一步它允许你在任何终端窗口直接输入adb命令而无需每次都切换到工具所在目录。Windows系统将解压后的platform-tools文件夹路径例如C:\android\platform-tools添加到“系统属性”-“高级”-“环境变量”中的用户或系统PATH变量里。macOS/Linux系统打开终端编辑你的shell配置文件如~/.bashrc或~/.zshrc添加一行export PATH$PATH:/path/to/platform-tools然后执行source ~/.bashrc使其生效。配置完成后打开一个新的终端或命令提示符输入adb version。如果正确显示了adb的版本号恭喜你第一步成功了。2.2 设备连接与授权跨越第一道鸿沟环境好了接下来是连接设备。使用USB数据线连接你的Android手机或平板到电脑。在设备上你需要开启“开发者选项”通常是在“关于手机”里连续点击“版本号”7次然后在其中开启“USB调试”功能。连接后在电脑终端执行adb devices。这时你可能会看到两种状态device表示设备已连接并授权。这是理想状态。unauthorized表示设备连接了但电脑未获得调试授权。如果看到unauthorized你需要在手机屏幕上弹出的“允许USB调试吗”的对话框中点击“确定”。这是一个安全机制确保只有你信任的电脑才能控制你的设备。授权后再次执行adb devices状态应变为device。注意有些厂商手机如华为、小米在开启USB调试后还需要在开发者选项里额外开启“USB调试安全设置”或关闭“监控ADB安装应用”否则adb install等命令可能失败。这是实际踩坑后总结的经验。对于无线调试可以先通过USB线连接然后使用adb tcpip 5555命令在设备上启动TCP/IP模式接着拔掉线使用adb connect 设备IP:5555进行无线连接。这在需要同时操作多台设备或手机不便插线时非常有用。2.3 基础命令验证确保通道畅通连接成功后强烈建议运行几个基础命令来验证整个交互通道是否完全畅通adb shell getprop ro.product.model获取设备型号。这可以立刻验证adb shell命令是否工作。adb install -r path/to/your.apk尝试安装一个测试APK-r参数表示覆盖安装。安装成功是后续所有自动化操作的前提。adb shell input keyevent 4模拟按下返回键KEYCODE_BACK。如果设备有反应如退出当前页面说明输入模拟命令有效。这些命令的顺利执行意味着你的adb“管道”已经铺设完毕可以开始向设备发送更复杂的指令了。3. 核心武器库必须掌握的adb自动化命令详解adb自动化的本质是将一系列手动操作转化为对应的命令序列。下面我们拆解最核心、最常用的几类命令并解释其背后的原理和使用技巧。3.1 应用生命周期管理安装、运行与清理这是自动化测试的起点和终点。安装应用adb install [options] apk-file-path-r替换现有应用保留数据。在持续测试中非常常用。-t允许安装测试APK。AndroidManifest中声明了android:testOnlytrue的应用需要此参数。-d允许降级安装版本号比已安装的低。实战技巧安装后最好用adb shell pm path package-name验证APK是否真的安装到了预期的路径。启动应用/Activityadb shell am start -n package-name/activity-name这是最标准的启动方式。你需要知道应用的主Activity名可以通过反编译APK或查看其AndroidManifest.xml获得。更常用的方式是adb shell monkey -p package-name -c android.intent.category.LAUNCHER 1。monkey虽然是压力测试工具但用它带-p参数指定包名并限制LAUNCHERcategory和事件数为1可以非常稳定地启动应用的主界面无需关心具体的Activity名。停止应用adb shell am force-stop package-name。这会强制停止应用进程相当于在设置里点击“强制停止”。在每次测试用例开始前执行可以确保应用从一个干净的状态启动。卸载应用adb uninstall package-name。清理测试环境。3.2 模拟用户输入让手指“消失”adb shell input命令是模拟用户交互的核心。点击屏幕adb shell input tap x y这里的x和y是屏幕坐标。如何获取坐标可以在开发者选项中开启“指针位置”屏幕上点击时就会显示坐标。更编程化的方式是结合adb shell uiautomator dump获取界面XML再解析元素的bounds属性来计算出中心点坐标。这是编写健壮脚本的关键不要依赖绝对坐标要依赖元素查找。输入文本adb shell input text “HelloWorld”注意此命令无法输入中文也无法输入包含空格除非用引号包裹整个字符串和一些特殊符号。对于复杂输入一个变通方法是先tap点击输入框然后使用adb shell input keyevent组合来模拟键盘操作但这非常繁琐。更实用的方案是对于测试应用直接通过adb shell am broadcast或adb shell content命令向应用注入预设好的测试数据绕过UI输入。模拟按键adb shell input keyevent keycode这是极其有用的命令。常用键值有3HOME键、4返回键、24音量加、25音量减、66回车键、82菜单键。你可以在Android官网查找完整的KeyEvent列表。在脚本中常用它来返回、退出弹窗或确认对话框。滑动adb shell input swipe x1 y1 x2 y2 [duration(ms)]从点(x1, y1)滑动到点(x2, y2)。可选的duration参数表示滑动耗时毫秒时间越长滑动越慢。这个命令常用于列表滚动、解锁屏幕、切换页面等。3.3 获取界面信息脚本的“眼睛”自动化脚本不能盲操作需要知道当前界面是什么有哪些元素。截图adb shell screencap -p /sdcard/screenshot.png然后adb pull /sdcard/screenshot.png .这是最直接的“看”的方式。可以将截图保存下来用于后续的人工比对或作为错误报告附件。注意screencap命令的输出是二进制数据直接重定向到文件即可-p参数表示PNG格式。录制屏幕adb shell screenrecord /sdcard/demo.mp4按CtrlC停止对于复现动态的Bug非常有用。录制结束后同样用adb pull拉取到电脑。获取当前界面XML布局adb shell uiautomator dump /sdcard/ui.xml这是高级自动化脚本的基石。这条命令会获取当前屏幕的UI层次结构并以XML格式保存。你可以用adb pull拉取这个XML文件然后解析它。XML里包含了所有可见控件的属性如resource-id、text、class、bounds坐标范围等。通过解析这个文件你的脚本就能“看到”屏幕上有一个“登录”按钮text登录并计算出它的坐标来执行tap操作。这比使用固定坐标要稳定得多因为应用UI改版后只要按钮的文本或ID没变你的脚本就依然能定位到它。3.4 文件与日志操作数据搬运与问题追溯文件传输adb push local remote和adb pull remote [local]自动化测试中常用于将测试配置文件、Mock数据推送到设备的sdcard目录或者将测试生成的日志、数据库文件拉取到电脑分析。日志抓取adb logcat这是排查测试过程中应用崩溃或异常的核心工具。在脚本中通常会在测试开始前清空日志adb logcat -c然后在测试执行的同时将日志输出重定向到文件adb logcat -v time test.log 。-v time参数为每行日志加上时间戳对分析问题发生顺序至关重要。抓取完毕后可以用adb shell killall logcat来结束后台的日志抓取进程。4. 从命令到脚本构建可维护的自动化流程掌握了单个命令就像有了散落的零件。接下来我们需要用脚本语言如Shell或Python把这些零件组装成一台可以自动运行的机器。4.1 Shell脚本轻量快速的胶水对于简单的、线性的任务Shell脚本Windows下可用批处理.bat是最高效的选择。它的优势是直接调用adb命令无需额外环境。#!/bin/bash # 一个简单的安装、启动、截图、卸载的Shell脚本示例 DEVICE_SERIALemulator-5554 # 指定设备序列号多设备时有用 APK_PATH./app-debug.apk PACKAGE_NAMEcom.example.myapp SCREENSHOT_NAMEscreenshot_$(date %Y%m%d_%H%M%S).png echo “1. 正在卸载旧应用...” adb -s $DEVICE_SERIAL uninstall $PACKAGE_NAME echo “2. 正在安装新APK...” adb -s $DEVICE_SERIAL install -r $APK_PATH echo “3. 启动应用...” adb -s $DEVICE_SERIAL shell monkey -p $PACKAGE_NAME -c android.intent.category.LAUNCHER 1 sleep 3 # 等待应用完全启动 echo “4. 执行一次点击操作 (假设坐标 500 800)...” adb -s $DEVICE_SERIAL shell input tap 500 800 sleep 1 echo “5. 截图并拉取...” adb -s $DEVICE_SERIAL shell screencap -p /sdcard/$SCREENSHOT_NAME adb -s $DEVICE_SERIAL pull /sdcard/$SCREENSHOT_NAME . echo “截图已保存为: $SCREENSHOT_NAME” echo “6. 测试完成清理...” adb -s $DEVICE_SERIAL shell am force-stop $PACKAGE_NAME这个脚本展示了基本的流程控制。其中-s参数用于指定设备序列号在多设备连接时至关重要。sleep命令用于等待操作完成这是一种简单的同步机制但在复杂场景下可能不可靠。4.2 Python脚本更强大的控制与解析对于需要逻辑判断、解析UI XML、处理文件等更复杂的任务Python是更佳选择。利用subprocess模块调用adb命令结合xml.etree.ElementTree解析UI dump文件可以构建出非常智能的脚本。import subprocess import xml.etree.ElementTree as ET import time import os def run_adb_command(command): 执行adb命令并返回输出 result subprocess.run(command, shellTrue, capture_outputTrue, textTrue) if result.returncode ! 0: print(f命令执行失败: {command}) print(f错误信息: {result.stderr}) return None return result.stdout.strip() def find_element_and_click(text): 通过文本查找元素并点击 # 1. 获取当前UI布局 run_adb_command(adb shell uiautomator dump /sdcard/ui.xml) run_adb_command(adb pull /sdcard/ui.xml .) # 2. 解析XML查找包含特定文本的节点 tree ET.parse(ui.xml) root tree.getroot() for node in root.iter(node): if text in node.attrib and node.attrib[text] text: bounds node.attrib[bounds] # 解析bounds字符串例如 “[42,120][180,156]” coords bounds.replace([, ).replace(], ,).split(,) x1, y1, x2, y2 map(int, coords[:4]) center_x (x1 x2) // 2 center_y (y1 y2) // 2 print(f找到元素 {text}坐标 ({center_x}, {center_y})) # 3. 执行点击 run_adb_command(fadb shell input tap {center_x} {center_y}) return True print(f未找到文本为 {text} 的元素) return False # 主流程 if __name__ __main__: package_name com.example.myapp # 强制停止并启动 run_adb_command(fadb shell am force-stop {package_name}) run_adb_command(fadb shell monkey -p {package_name} -c android.intent.category.LAUNCHER 1) time.sleep(3) # 智能点击“登录”按钮 if find_element_and_click(登录): time.sleep(1) # 后续可以继续查找“用户名”输入框并输入文本等... print(“登录按钮点击成功流程继续。”) else: print(“流程中断未找到登录按钮。”) # 最终截图 screenshot_name fscreenshot_{int(time.time())}.png run_adb_command(fadb shell screencap -p /sdcard/{screenshot_name}) run_adb_command(fadb pull /sdcard/{screenshot_name} .)这个Python脚本展示了自动化测试的一个核心思想基于状态的交互。它不是盲目地点击固定坐标而是先获取当前界面状态UI Dump解析出目标元素的位置然后再行动。这大大提高了脚本的健壮性和可维护性。当UI布局发生变化但按钮文本未变时脚本依然能正常工作。4.3 脚本健壮性增强等待、重试与错误处理纯adb脚本最大的挑战在于处理应用响应的不确定性。网络加载、动画效果都可能导致脚本执行过快而失败。显式等待简单的time.sleep是脆弱的。更好的方法是在关键操作后循环检查某个条件是否满足。例如点击登录后可以循环检查当前UI中是否出现了“欢迎”文本或者是否跳转到了新的Activity通过adb shell dumpsys window windows | grep -E mCurrentFocus|mFocusedApp判断。操作重试对于非关键性失败如因动画未完成导致的点击无效可以封装一个带重试机制的点击函数在失败后等待片刻再重试最多3次。完善的日志脚本的每一步操作尤其是关键判断和命令执行结果都应该输出到日志文件。这就像飞机的黑匣子当测试失败时你可以通过日志清晰地看到脚本执行到了哪一步当时设备的状态是什么从而快速定位问题。设备状态检查在脚本开始和关键节点检查设备是否在线adb get-state、应用包是否已安装、所需权限是否已授予。提前发现环境问题避免无意义的执行。5. 实战进阶组合命令解决复杂测试场景掌握了基础命令和脚本框架我们可以挑战更复杂的真实测试需求。5.1 场景一跨应用交互测试测试一个分享功能从图库选择一张图片分享到你的应用。# 假设你的应用包名为 com.our.app # 1. 启动系统图库不同系统包名不同这里以假设的为例 adb shell am start -n com.android.gallery3d/.app.GalleryActivity sleep 2 # 2. 模拟点击选择第一张图片这里需要根据实际UI获取坐标或使用uiautomator dump定位 adb shell input tap 200 400 sleep 1 # 3. 点击分享按钮 adb shell input tap 1000 200 sleep 2 # 4. 在分享列表中找到你的应用并点击这步最复杂通常需要解析分享列表的UI # 一种方法是先dump分享列表的UI找到包含“Our App”文本的节点计算坐标点击。 # 另一种取巧方法如果你的应用在分享列表中是固定的位置可以用固定坐标。 adb shell input tap 300 600 # 如果成功你的应用应该被启动并接收到图片数据。这个场景的难点在于分享列表是动态的且不同手机系统差异极大。最稳健的方式还是通过uiautomator dump来解析分享列表的UI。5.2 场景二性能数据采集与监控在自动化执行功能用例的同时采集CPU、内存等性能数据。# 在测试开始前获取目标应用的进程IDPID APP_PID$(adb shell pidof com.example.myapp) # 如果应用未启动pidof可能为空需要先启动 if [ -z $APP_PID ]; then adb shell monkey -p com.example.myapp 1 sleep 3 APP_PID$(adb shell pidof com.example.myapp) fi # 开始后台收集内存信息每2秒一次共收集10次 adb shell for i in \$(seq 1 10); do adb shell dumpsys meminfo $APP_PID | grep -E TOTAL|App Summary; sleep 2; done memory.log # 执行你的功能测试脚本 ./run_functional_test.sh # 测试结束后停止内存收集实际上上面的循环会自动结束 # 同样方法可以收集CPUadb shell top -n 1 -p $APP_PID将性能采集脚本与功能测试脚本并行运行你就能得到一组与具体操作相关联的性能数据对于发现特定操作导致的内存泄漏或CPU峰值非常有帮助。5.3 场景三多设备并行测试如果你有多个测试设备比如不同分辨率、不同Android版本串行执行脚本会非常耗时。可以利用Shell或Python的进程并发机制实现简单的并行测试。import subprocess import threading device_list [“emulator-5554”, “ABCDEF0123456789”] # 设备序列号列表 apk_path “./app-debug.apk” def run_test_on_device(device_serial): 在一个设备上运行测试的独立函数 print(f“开始在设备 {device_serial} 上执行测试...”) subprocess.run(f“adb -s {device_serial} install -r {apk_path}”, shellTrue) subprocess.run(f“adb -s {device_serial} shell monkey -p com.example.myapp 1”, shellTrue) time.sleep(2) # ... 执行更多的测试步骤 print(f“设备 {device_serial} 测试完成。”) threads [] for device in device_list: t threading.Thread(targetrun_test_on_device, args(device,)) t.start() threads.append(t) for t in threads: t.join() # 等待所有线程结束 print(“所有设备测试执行完毕。”)这样你的测试套件可以在所有设备上同时运行测试时间从线性叠加减少到最慢的那台设备所花费的时间。6. 避坑指南与效能提升来自实战的经验之谈在大量使用adb自动化脚本后我积累了一些“血泪教训”这些经验能让你的脚本从“能用”变得“好用且可靠”。6.1 稳定性陷阱异步世界的同步难题移动应用是高度异步的。网络请求、动画、页面渲染都需要时间。脚本最常见的失败原因就是“跑得太快”。不要迷信sleep固定的sleep时间在慢设备或网络差时可能不够在快设备上又浪费生命。它只应用于最粗略的等待。建立“就绪”检查点在关键操作后如点击登录、跳转页面编写检查函数。例如等待一个特定的UI元素出现通过循环调用uiautomator dump并解析或者等待某个Activity成为前台Activity解析dumpsys window windows的输出。这称为“显式等待”。设置超时与重试任何等待都应该有一个超时时间比如30秒。如果超时脚本可以记录错误、截图然后尝试重试操作或直接标记用例失败。这避免了脚本因一个偶然的卡顿而无限期挂起。6.2 兼容性挑战碎片化的Android世界不同厂商、不同系统版本的设备其系统UI和adb行为可能存在细微差别。慎用绝对坐标这是最大的兼容性杀手。一个在1080P屏幕上(500, 500)的按钮在2K屏上可能完全不在那个位置。始终坚持通过resource-id或text等属性来定位元素如果必须用坐标请使用基于屏幕百分比的计算方式。注意权限弹窗在Android 6.0API 23及以上版本运行时权限弹窗会打断自动化流程。脚本中需要加入处理这些系统弹窗的逻辑。通常可以通过查找弹窗中“允许”或“拒绝”按钮的文本来点击。更彻底的方法是在测试前通过adb shell pm grant命令预先授予应用所需的所有权限。输入法的干扰执行input text时如果输入法键盘弹出可能会遮挡UI元素。一个技巧是在输入前先发送一个返回键事件input keyevent 4来关闭可能弹出的键盘或者使用adb shell ime set命令切换到特定的输入法如空的输入法。6.3 脚本的可维护性让它易于阅读和修改脚本不是一次性的需要被你和你的团队反复使用和修改。配置与代码分离将设备序列号、应用包名、关键坐标如果必须、等待超时时间等变量提取到配置文件如config.ini或脚本开头的常量定义区。修改配置时无需动核心逻辑。模块化函数将重复的操作封装成函数如install_app(),click_by_text(),wait_for_activity()。主流程脚本会变得非常清晰像阅读测试用例文档一样。详尽的日志输出在每一个步骤开始和结束时都输出明确的日志包括时间戳。不仅输出“做了什么”在可能失败的地方还要输出“看到了什么”如dump出的当前界面关键信息。当测试在夜间失败时这些日志是你唯一的诊断依据。版本控制像对待源代码一样对待你的测试脚本使用Git进行版本管理。这可以追踪脚本的变更并与应用的不同版本对应起来。6.4 效能提升从脚本到简易框架当脚本越来越多你会发现一些共同的需求这时可以抽象出一个简单的框架层。用例管理用YAML或JSON文件来描述一个测试用例包含步骤列表、预期结果如应出现的文本、清理操作等。脚本作为执行引擎来读取并执行这些用例文件。结果报告脚本不应只输出控制台日志。可以生成结构化的测试报告如JUnit XML格式这样可以被Jenkins等CI/CD工具集成并以图表形式展示测试通过率、失败历史等。与CI/CD集成将你的adb自动化脚本作为Jenkins Pipeline或GitLab CI的一个阶段。每次代码提交后自动在连接的测试设备上运行冒烟测试脚本快速反馈基础功能是否被破坏。adb命令的自动化起点很低但天花板可以很高。它可能无法替代专业的UI测试框架进行复杂的交互验证但在设备控制、应用部署、简单流程回归和兼容性快速检查方面它拥有无与伦比的轻量、直接和高效。花时间掌握它就像是给你的测试武器库添加了一把多功能瑞士军刀在很多场景下它往往是最顺手、最快速的解决方案。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门