杰瑞科技汇

monkeyrunner教程

Monkeyrunner 教程:从入门到实战

目录

  1. 什么是 Monkeyrunner?
  2. 环境搭建
  3. 核心概念:Monkeyrunner API
  4. 编写第一个脚本
  5. 常用 API 深入解析
    • 连接设备
    • 安装/卸载 APK
    • 运行应用
    • 发送用户事件(点击、滑动、输入)
    • 截图
    • 断言
  6. 实战案例
    • 案例 1:自动启动 App 并点击按钮
    • 案例 2:实现一个简单的 UI 自动化测试
    • 案例 3:获取屏幕上的文本内容
  7. Monkeyrunner vs. UI Automator vs. Espresso
  8. 最佳实践与注意事项

什么是 Monkeyrunner?

Monkeyrunner 是 Google 官方提供的一个基于 Jython (Python for Java) 的自动化测试工具,它主要用于 Android 应用的 功能测试、回归测试和 UI 测试

monkeyrunner教程-图1
(图片来源网络,侵删)

核心特点:

  • 基于 Python:使用 Python 语言编写脚本,语法简单,上手快。
  • 多设备支持:可以同时连接和控制多台 Android 设备或模拟器。
  • API 丰富:提供了强大的 API 来模拟用户操作(点击、滑动、按键等)、安装应用、截图等。
  • 流程控制:可以将多个设备操作组合成一个复杂的测试流程。

工作流程:

  1. 你编写一个 Python 脚本。
  2. 使用 monkeyrunner 命令行工具执行该脚本。
  3. monkeyrunner 工具启动一个 Jython 解释器。
  4. Jython 解释器加载并执行你的脚本。
  5. 脚本通过 Monkeyrunner API 与 Android 设备/模拟器进行交互,执行测试操作。

环境搭建

在使用 Monkeyrunner 之前,你需要确保你的开发环境已经配置好。

前提条件:

monkeyrunner教程-图2
(图片来源网络,侵删)
  • 已安装 JDK (Java Development Kit)。
  • 已安装 Android SDK
  • 已配置好 ANDROID_HOME (或 ANDROID_SDK_ROOT) 环境变量。

验证环境:

打开你的终端或命令提示符,输入以下命令:

adb version

如果能看到 ADB 的版本信息,说明你的基本环境已经配置好了。

启动 Monkeyrunner:

ANDROID_HOME/tools 目录下(较新版本的 SDK 可能在 tools/bincmdline-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 模块中:

  1. MonkeyRunner:提供辅助功能,如连接设备、休眠等。

    • waitForConnection([timeout, device_id]): 连接设备,可以指定超时时间和设备序列号。
    • sleep(seconds): 让脚本暂停指定的秒数。
  2. 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 对象。
  3. 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 由于其局限性和维护性问题,已不再是首选,但对于一些遗留项目或简单的自动化任务,它仍然是一个可用的工具。


最佳实践与注意事项

  1. 避免硬编码坐标:坐标是屏幕分辨率相关的,一旦屏幕尺寸或布局改变,脚本就会失效。尽可能使用 UI Automator 的方式来定位控件,如果必须用坐标,考虑通过 adb shell wm size 获取分辨率,然后按比例计算。
  2. 增加等待时间:应用启动、界面加载都需要时间,使用 MonkeyRunner.sleep() 或更智能的等待(如轮询某个控件是否存在)来避免因速度过快导致的失败。
  3. 模块化和封装:将常用的操作(如登录、点击某个按钮)封装成函数或类,提高代码复用性和可读性。
  4. 错误处理:使用 try...except 块来捕获和处理可能发生的异常(如设备连接中断、元素找不到等),使脚本更健壮。
  5. 清晰的日志:使用 print() 语句输出清晰的日志,方便调试和追踪脚本执行流程。
  6. 版本控制:将你的测试脚本和相关的资源文件(如预期截图)纳入版本控制系统(如 Git)。

Monkeyrunner 是一个功能强大且相对简单的 Android 自动化测试工具,尤其适合那些熟悉 Python 的开发者,它能够帮助你快速实现一些基于坐标的 UI 自动化流程。

随着 Android 测试技术的发展,Monkeyrunner 的局限性也日益明显,如果你正在开始一个新的测试项目,强烈建议你学习更现代、更稳定的框架,如 EspressoUI Automator

希望这份教程能帮助你快速上手 Monkeyrunner!

分享:
扫描分享到社交APP
上一篇
下一篇