Monkeyrunner 教程:从入门到实战
目录
- 什么是 Monkeyrunner?
- 环境搭建
- 核心概念:Monkeyrunner API
- 编写第一个脚本
- 常用 API 深入解析
- 连接设备
- 安装/卸载 APK
- 运行应用
- 发送用户事件(点击、滑动、输入)
- 截图
- 断言
- 实战案例
- 案例 1:自动启动 App 并点击按钮
- 案例 2:实现一个简单的 UI 自动化测试
- 案例 3:获取屏幕上的文本内容
- Monkeyrunner vs. UI Automator vs. Espresso
- 最佳实践与注意事项
什么是 Monkeyrunner?
Monkeyrunner 是 Google 官方提供的一个基于 Jython (Python for Java) 的自动化测试工具,它主要用于 Android 应用的 功能测试、回归测试和 UI 测试。

核心特点:
- 基于 Python:使用 Python 语言编写脚本,语法简单,上手快。
- 多设备支持:可以同时连接和控制多台 Android 设备或模拟器。
- API 丰富:提供了强大的 API 来模拟用户操作(点击、滑动、按键等)、安装应用、截图等。
- 流程控制:可以将多个设备操作组合成一个复杂的测试流程。
工作流程:
- 你编写一个 Python 脚本。
- 使用
monkeyrunner命令行工具执行该脚本。 monkeyrunner工具启动一个 Jython 解释器。- Jython 解释器加载并执行你的脚本。
- 脚本通过 Monkeyrunner API 与 Android 设备/模拟器进行交互,执行测试操作。
环境搭建
在使用 Monkeyrunner 之前,你需要确保你的开发环境已经配置好。
前提条件:

- 已安装 JDK (Java Development Kit)。
- 已安装 Android SDK。
- 已配置好
ANDROID_HOME(或ANDROID_SDK_ROOT) 环境变量。
验证环境:
打开你的终端或命令提示符,输入以下命令:
adb version
如果能看到 ADB 的版本信息,说明你的基本环境已经配置好了。
启动 Monkeyrunner:
在 ANDROID_HOME/tools 目录下(较新版本的 SDK 可能在 tools/bin 或 cmdline-tools/latest/bin),你可以直接运行 monkeyrunner 命令来启动交互式环境。
# 假设你的 ANDROID_HOME 是 /Users/yourname/Library/Android/sdk cd /Users/yourname/Library/Android/sdk/tools ./monkeyrunner
进入交互式环境后,你可以直接输入 Python 代码来测试 API。
# 交互式环境中测试
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
# 获取连接的设备
device = MonkeyRunner.waitForConnection()
# 打印设备信息
print(device.getProperty('ro.build.version.release'))
核心概念:Monkeyrunner API
Monkeyrunner 的核心功能都封装在三个主要的 Jython 模块中:
-
MonkeyRunner:提供辅助功能,如连接设备、休眠等。waitForConnection([timeout, device_id]): 连接设备,可以指定超时时间和设备序列号。sleep(seconds): 让脚本暂停指定的秒数。
-
MonkeyDevice:代表一个 Android 设备,是执行操作的主要接口。touch(x, y, 'DOWN_AND_UP'): 模拟触摸事件。drag(startX, startY, endX, endY, duration, steps): 模拟拖动/滑动事件。press(key, 'DOWN_AND_UP'): 模拟按键事件。type(string): 输入文本。install(path): 安装 APK。uninstall(package_name): 卸载应用。start_activity(component): 启动一个 Activity。shell(command): 执行 ADB shell 命令。takeSnapshot(): 截图,返回一个MonkeyImage对象。
-
MonkeyImage:代表一张截图,用于图像比较和保存。writeToFile(path, format): 将截图保存到文件。sameAs(image, percent): 比较两张图片是否相似(可选相似度百分比)。
编写第一个脚本
让我们从一个最简单的脚本开始:连接设备,然后截图并保存到本地。
脚本 first_script.py:
# 导入所需的模块
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
# 1. 连接设备 (等待5秒)
print("正在连接设备...")
device = MonkeyRunner.waitForConnection(5.0)
# 检查是否成功连接
if not device:
print("无法连接到设备,请检查设备是否已连接并启用调试模式。")
exit(1)
print("设备连接成功!")
# 2. 截图
print("正在截图...")
snapshot = device.takeSnapshot()
# 3. 保存截图到本地
snapshot.writeToFile('/tmp/device_screen.png', 'png')
print("截图已保存到 /tmp/device_screen.png")
# 4. 退出脚本
print("脚本执行完毕。")
执行脚本:
在终端中,使用 monkeyrunner 命令来执行你的脚本。
# 确保你的设备已连接并启用 USB 调试 # 在 tools 目录下执行 ./monkeyrunner your_script_path/first_script.py
执行成功后,你会在指定的路径(这里是 /tmp/)下找到 device_screen.png 文件。
常用 API 深入解析
a. 连接设备
MonkeyRunner.waitForConnection() 是最常用的连接方式。
# 连接第一个找到的设备,超时10秒 device = MonkeyRunner.waitForConnection(10.0) # 连接特定序列号的设备 # device_serial = 'emulator-5554' # 可以通过 'adb devices' 查看 # device = MonkeyRunner.waitForConnection(10.0, device_serial)
b. 安装/卸载 APK
# 安装 APK
device.install('/path/to/your/app.apk')
# 卸载 APK (需要包名)
device.uninstall('com.example.myapp')
c. 运行应用
启动应用通常需要指定 Component Name,格式为 package_name/activity_name,你可以在 AndroidManifest.xml 中找到,或者使用 adb shell dumpsys window 命令来获取当前顶层 Activity。
# 启动一个 Activity package = 'com.android.settings' activity = '.Settings' component = package + '/' + activity device.startActivity(component=component) # 或者使用 Intent # device.startActivity(component='com.android.settings/.Settings')
d. 发送用户事件
这是 UI 自动化的核心。
-
点击:
touch(x, y, type)type可以是'DOWN','UP','DOWN_AND_UP',通常使用'DOWN_AND_UP'来模拟一次完整的点击。
# 点击屏幕坐标 (500, 500) device.touch(500, 500, MonkeyDevice.DOWN_AND_UP)
-
滑动:
drag(startX, startY, endX, endY, duration, steps)duration: 滑动持续时间(毫秒)。steps: 滑动过程被分成的步数,步数越多,滑动越平滑。
# 从 (100, 500) 滑动到 (500, 500),持续 1 秒,分 10 步完成 device.drag(100, 500, 500, 500, 1.0, 10)
-
输入文本:
type(string)- 注意:输入前需要确保焦点在可输入的文本框上(先点击文本框)。
# 点击一个文本框(假设坐标为 (200, 300)) device.touch(200, 300, MonkeyDevice.DOWN_AND_UP) # 输入 "Hello, Monkeyrunner!" device.type("Hello, Monkeyrunner!") -
按键:
press(key, type)key是按键的常量,如MonkeyDevice.KEY_MENU,MonkeyDevice.KEY_BACK,MonkeyDevice.KEY_HOME等。
# 按下返回键 device.press(MonkeyDevice.KEY_BACK, MonkeyDevice.DOWN_AND_UP) # 按下 Home 键 device.press(MonkeyDevice.KEY_HOME, MonkeyDevice.DOWN_AND_UP)
e. 截图
takeSnapshot() 返回一个 MonkeyImage 对象,可以用于后续的比较或保存。
# 截图
current_screen = device.takeSnapshot()
# 保存
current_screen.writeToFile('/path/to/current_screen.png', 'png')
f. 断言
Monkeyrunner 本身不提供像 unittest 那样的断言库,但你可以通过截图比较来实现简单的断言。
# 假设我们期望的截图已经保存
expected_image = MonkeyRunner.loadImageFromFile('/path/to/expected_screen.png')
# 获取当前屏幕截图
actual_image = device.takeSnapshot()
# 比较两张图片,如果相似度低于99%,则认为测试失败
if not actual_image.sameAs(expected_image, 0.99):
print("测试失败:当前屏幕与预期屏幕不匹配!")
# 可以在这里保存失败时的截图
actual_image.writeToFile('/path/to/failure_screen.png', 'png')
# 退出脚本,返回非零状态码表示失败
exit(1)
else:
print("测试通过:当前屏幕与预期屏幕匹配!")
实战案例
案例 1:自动启动 App 并点击按钮
假设我们要启动计算器 App,并点击 "5" 这个按钮。
脚本 calculator_test.py:
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
# 连接设备
device = MonkeyRunner.waitForConnection()
if not device:
print("连接设备失败!")
exit(1)
# 启动计算器
print("启动计算器...")
device.startActivity(component='com.android.calculator2/.Calculator')
# 等待应用启动 (2秒)
MonkeyRunner.sleep(2)
# 点击 "5" 按钮 (需要根据实际屏幕分辨率调整坐标)
# 假设 "5" 按钮的坐标是 (300, 800)
print("点击 '5' 按钮...")
device.touch(300, 800, MonkeyDevice.DOWN_AND_UP)
# 再次截图保存
device.takeSnapshot().writeToFile('/tmp/calculator_result.png', 'png')
print("截图已保存。")
案例 2:实现一个简单的 UI 自动化测试
这个案例将启动一个 App,执行一系列操作,然后通过截图来验证结果。
场景:打开设置 -> 点击 "关于手机" -> 验证页面标题是否正确。
脚本 settings_test.py:
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
# --- 初始化 ---
device = MonkeyRunner.waitForConnection(5.0)
if not device:
print("连接设备失败!")
exit(1)
# --- 测试步骤 ---
# 1. 启动设置
print("启动设置...")
device.startActivity(component='com.android.settings/.Settings')
MonkeyRunner.sleep(2) # 等待界面加载
# 2. 点击 "关于手机"
# 坐标需要根据你的设备分辨率调整
print("点击 '关于手机'...")
device.touch(100, 800, MonkeyDevice.DOWN_AND_UP)
MonkeyRunner.sleep(1)
# 3. 获取当前屏幕截图
actual_screen = device.takeSnapshot()
# 4. 断言:与预期截图进行比较
expected_screen = MonkeyRunner.loadImageFromFile('expected_about_phone.png') # 你需要提前准备好这张图
print("进行图像比对...")
if actual_screen.sameAs(expected_screen, 0.95): # 95%相似度
print("测试通过:'关于手机'页面显示正确。")
else:
print("测试失败:'关于手机'页面显示不正确!")
actual_screen.writeToFile('failure_about_phone.png', 'png')
exit(1)
# --- 清理 ---
print("测试脚本执行完毕。")
案例 3:获取屏幕上的文本内容
重要提示:Monkeyrunner 原生不支持 直接从 UI 层获取文本内容,这是一个常见的痛点。
解决方案:通过执行 adb shell 命令,利用 Android 的辅助服务(如 uiautomator)来获取文本。
脚本 get_text_example.py:
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
# 连接设备
device = MonkeyRunner.waitForConnection()
if not device:
print("连接设备失败!")
exit(1)
# 启动一个有文本的 App,比如设置
device.startActivity(component='com.android.settings/.Settings')
MonkeyRunner.sleep(2)
# 使用 adb shell dumpsys 命令获取窗口信息,并从中解析文本
# 这是一个比较复杂的方法,需要解析文本输出
# 获取当前 Activity 的标题
command = "dumpsys window windows | grep mCurrentFocus"
output = device.shell(command)
# 从输出中解析包名和类名
# 格式通常是: mCurrentFocus=Window{... com.android.settings/.Settings}
# 这是一个简化的例子,实际解析可能更复杂
if "com.android.settings/.Settings" in output:
print("成功获取到当前 Activity: Settings")
# 更精确的获取文本需要使用 UIAutomator2,这通常在 Python 的 uiautomator2 库中实现
# Monkeyrunner 本身做起来很麻烦。
# 替代方案:使用 Accessibility Service
# 但这需要 App 自身实现,或者通过 Monkeyrunner 启动一个辅助服务,超出了基础教程的范围。
print("Monkeyrunner 原生获取文本非常困难,推荐使用 uiautomator2 库。")
Monkeyrunner vs. UI Automator vs. Espresso
| 特性 | Monkeyrunner | UI Automator | Espresso |
|---|---|---|---|
| 语言 | Jython (Python) | Java / Kotlin | Java / Kotlin |
| 用途 | 黑盒测试,跨应用测试 | 黑盒测试,跨应用测试 | 白盒测试,应用内测试 |
| 控制粒度 | 设备级别(坐标点击) | UI 控件级别(ID, Text, Class) | UI 控件级别(View Matcher) |
| 依赖 | Android SDK | Android SDK + androidx.test.uiautomator |
Android SDK + androidx.test.espresso |
| 优点 | Python 语法简单,上手快,适合快速原型和简单流程控制。 | 真正的跨应用测试,不依赖内部实现,稳定可靠。 | 速度快,与 App 生命周期集成好,适合单元测试和集成测试。 |
| 缺点 | 坐标依赖,维护成本高;功能相对有限;Python 生态在 Android 测试中不主流。 | 需要编写 Java/Kotlin 代码;启动速度比 Espresso 慢。 | 白盒测试,需要了解 App 内部代码;难以进行跨应用测试。 |
| 推荐场景 | 简单的 UI 流程自动化、压力测试、快速验证。 | 跨应用测试(如启动系统相机、选择文件)、需要与系统组件交互的测试。 | 应用内单元测试、集成测试、UI 测试(最主流的 Android UI 测试框架)。 |
对于新项目,Espresso + UI Automator 是官方推荐的最佳实践组合,Monkeyrunner 由于其局限性和维护性问题,已不再是首选,但对于一些遗留项目或简单的自动化任务,它仍然是一个可用的工具。
最佳实践与注意事项
- 避免硬编码坐标:坐标是屏幕分辨率相关的,一旦屏幕尺寸或布局改变,脚本就会失效。尽可能使用 UI Automator 的方式来定位控件,如果必须用坐标,考虑通过
adb shell wm size获取分辨率,然后按比例计算。 - 增加等待时间:应用启动、界面加载都需要时间,使用
MonkeyRunner.sleep()或更智能的等待(如轮询某个控件是否存在)来避免因速度过快导致的失败。 - 模块化和封装:将常用的操作(如登录、点击某个按钮)封装成函数或类,提高代码复用性和可读性。
- 错误处理:使用
try...except块来捕获和处理可能发生的异常(如设备连接中断、元素找不到等),使脚本更健壮。 - 清晰的日志:使用
print()语句输出清晰的日志,方便调试和追踪脚本执行流程。 - 版本控制:将你的测试脚本和相关的资源文件(如预期截图)纳入版本控制系统(如 Git)。
Monkeyrunner 是一个功能强大且相对简单的 Android 自动化测试工具,尤其适合那些熟悉 Python 的开发者,它能够帮助你快速实现一些基于坐标的 UI 自动化流程。
随着 Android 测试技术的发展,Monkeyrunner 的局限性也日益明显,如果你正在开始一个新的测试项目,强烈建议你学习更现代、更稳定的框架,如 Espresso 和 UI Automator。
希望这份教程能帮助你快速上手 Monkeyrunner!
