MonkeyRunner 自动化测试 Android 应用入门示例
这里总结如何使用 monkeyrunner
来自动化测试 android apk
应用。
1. 环境准备
1.1 python
安装
下载 python
的 msi
包,直接点击 next
,安装。
1.2 java jdk
安装
因为 android sdk
这些是基于 java jvm
,所以需要需要 java jdk
环境
1.3 android sdk
安装
使用 android sdk manager
管理工具,来安装 android sdk tools
和 android sdk platform_tools
。这里也需要安装一个版本的 sdk platform
,
截图如下:
由于 GWF
的原因,使用 google
的源安装 sdk
比较困难,我们可以换成国内的源。点击这里
1.4 windows
下配置环境变量
monkeyrunner
是 android platform_tools
中提供的一个自动化测试接口工具。可以通过脚本来模拟 APP
业务流程。
这里配置
JAVA_HOME=[你的 Java jre 文件存放根路径] ANDROID_HOME=[你的 Android sdk 文件存放根路径] Path=%JAVA_HOME%\bin;%JAVA_HOME%\lib;%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools;C\python
这里这样设置,是为了可以在 dos
情形下,直接执行对应的命令,如 python
, monkeyrunner
。
至此,环境搭建成功。
2. MonkeyRunner 是什么
monkeyrunner
是一个测试工具,通过运行 python
脚本来模拟 Android
手机界面的点击事件来测试。一般测试步骤:
模拟界面点击事件 --> 事件结果截图 --> 截图与正确图片比较来判断测试结果是否通过
来一段简单的 python
测试脚本:
# hello.py # coding: utf-8 from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage # 连接设备 device = MonkeyRunner.waitForConnection([delay secs], [deviceid]); # 安装APP apkFilePath = "E:/myapp.apk"; device.installPackage(apkFilePath); # 启动 APP # 自行 google package 和 activity 是什么 package = "com.test.myapp"; activity = "com.test.Android.myappActivity"; runComponent = package + "/" + activity; device.startActivity(component=runComponent); # 这里一般让脚本暂停一段时间,使得APP成功启动起来 MonkeyRunner.sleep(3); # 下面开始模拟界面事件 # 1. 点击事件 device.touch(x, y, "DOWN_AND_UP"); MonkeyRunner.sleep(1); # 2. 输入事件 # 先发焦点聚焦到输入控件上,然后 type 内容 device.touch(x, y, "DOWN_AND_UP"); MonkeyRunner.sleep(1); device.type(value); MonkeyRunner.sleep(1); # 3. 模拟按键按下,如后退,home按键按下 # 这里第一个参数值的定义可以 devie.press("KEYCODE_BACK", "DOWN_AND_UP"); MonkeyRunner.sleep(1); # 4. 界面滑动 device.drag((x1, y1), (x2, y2)); MonkeyRunner.sleep(1); # 点击事件触发,界面开始变化,截屏比较 imagetobecompared = MonkeyRunner.loadImageFromFile([image file path]); screenshot = device.takeSnapshot(); if screenshot.sameAs(imagetobecompared, 0.9): print "2张图片相同"; else: print "2张图片不相同";
代码中的 KEYCODE_BACK
这样的取值,可以参考
按照上方测试流程,我们需要做什么:
正确的截图如何获取并保存下来
设定测试模拟器分辨率,然后获取控件坐标
(x, y)
缺陷有哪些:
来点击控件是通过坐标来确定的,这样在不同分辨率的情形下,这个坐标是不一样的,测试不同分辨率机型要写不同的测试脚本
图片的比对,这个不怎么准确
3. 我们需要做什么
android sdk
下提供了一些工具来帮助我们获取界面中控件的坐标,例如 uiautomatorviewer
, hierarchyviewer
, monitor
。
3.1 uiautomatorviewer
这个可以打开后,通过点击界面上的控件,来获取控件的坐标。直接在 dos
下运行命令:
uiautomatorviewer
执行后效果图:
这里可以看到 bounds
属性的值 [256, 165][320, 223]
。这个是控件的左上和右下点的坐标,可以通过这2个点计算出中心点的坐标。
获取一个控件的坐标都这么麻烦。。。
3.2 MonkeyRecorder
这个工具提供了一套记录用户操作界面行为的操作,例如 TOUCH
, PRESS
, TYPE
, Drag
这些操作。这个工具在 tools
或者 platform-tools
下都没有看到,不过可以通过代码来调用。
# coding: utf-8 from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage from com.android.monkeyrunner.recorder import MonkeyRecorder as recorder device = MonkeyRunner.waitForConnection(3, "127.0.0.1:30054"); # 启动 recorder recorder.start(device);
启动后的截图如下:
点击界面上的控件,或者 type something
, 或者 Fling
这些操作,都会在右侧中留下操作记录,这里有点击点的坐标,
或者 type value
这些操作。
导出操作到文件中,在 py
脚本文件中,实现起来就方便了。(强烈推荐这个来记录操作流程)
3.3 如何保存正确的截屏文件用于后续的比对
目前没有发现比较方便快捷的截屏保存操作。所以在这里实现了一小段截屏脚本,使用流程:
启动模拟器,安装APP并启动我们需要测试的APP
运行我们的脚本
mkr console.py
,这里有个dos
窗口在模拟器中操作APP,如果需要截屏,在步骤2中打开的
dos
窗口中输入save
,保存截图
附上代码:
mkr.bat
文件内容
@echo off monkeyrunner %~dp0%1
console.py
文件内容
# console.py # coding: utf-8 # 连接模拟器 ... # 这里开始根据输入的命令执行对应的操作 # save: 截屏并保存 png 图片 # quit:退出 cmdstr = raw_input(">>>"); while cmdstr != "quit": # 截图保存 if cmdstr == "save": fpng = time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".png"; de.saveImage(os.path.join(pydir, "images", fpng), "png"); cmdstr = raw_input(">>>");
注:
这里运行 raw_input
命令的时候会卡主。参考下面的 Q&A
部分,问题1。
4. 如何对付上述流程中的缺陷
4.1 控件坐标问题
使用空间坐标来操作控件,当屏幕分辨率改变时,控件坐标自然会改变。难道我们每个坐标都写一个测试脚本。
这里有2种解决方式:
先固定一个屏幕分辨率,然后在新分辨率下重新计算控件坐标
使用控件
id
来操作控件
方式1,假定我们的测试脚本是在 320 * 568
分辨率下控制控件 (120, 200)
来做点击操作。原来的代码
device.touch(120, 200, "DOWN_AND_UP");
现在需要改为
dw = device.getProperty("display.width"); dh = device.getProperty("display.height"); nx = int(120 / 320.0 * int(dw)); ny = int(200 / 320.0 * int(dh));
另外一种方式是,通过控件 id
来获取控件,操作控件。对应的工具 hierarchyviewer
4.2 hierarchyviewer
在 dos
窗口下输入
> hierarchyviewer
运行该命令的时候,最好先把模拟器的界面调整到你需要取控件 id
的控件。上方命令成功后,会有如下截图:
这时选中 activity
点击 Load View Hierarchy
显示如下界面:
这里看到有的控件的下方有 id/content
这样的内容,这个就是控件的 id
。
使用代码示例如下:
# coding: utf-8 from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage from com.android.chimpchat.hierarchyviewer import HierarchyViewer from com.android.monkeyrunner.easy import By device = MonkeyRunner.waitForConnection(1, "127.0.0.1:30054"); hdevice = device.getHierarchyViewer(); # 通过id获取node vnode = hdevice.findViewById("id/content"); # 通过node,获取位置 point = hdevice.getAbsolutePositionOfView(vnode); pcenter = hdevice.getAbsoluteCenterOfView(vnode); # 获取node中的文本内容 txt = hdevice.getText(vnode);
4.3 EasyMonkeyDevice
# coding: utf-8 ... from com.android.monkeyrunner.easy import EasyMonkeyDevice from com.android.monkeyrunner.easy import By # 连接设备,启动APP ... easydevice = EasyMonkeyDevice(device); easydevice.touch(By.id("id/content"), MonkeyDevice.DOWN_AND_UP);
Q & A
1. monkeyrunner
raw_input()
接收输入后不运行后面的代码
这个被发现是 jpython
的一个 bug
, python
版本 jython-standalone-2.5.3.jar
。
当前你使用的 jpython
版本可以在 ${ANDROID_HOME}\tools\lib
下可以看到。
fix:
https://code.google.com/p/android/issues/detail?id=56318
2. 使用天天模拟器创建一个虚拟设备,使用 adb devices
检查不出来
这里是因为我们要手动连接天天模拟器,这里需要 ip
和 port
。
从上面的截图,可以看到文件夹 TianTian
, TianTian_1
, TianTian_2
...
这里的每一个文件夹内容表示一个模拟器,我这里创建了3个模拟器,所以有三个文件夹。要连接哪个模拟器,查看对应
模拟器下的连接 ip
地址,可以查看下面内容:
<!--/TianTian.vbox--> <VirtualBox ...> <Machine ...> ... <Hardware ...> ... <Network> <Adapter ...> ... <NAT> ... <Forwarding name="AdbPort" proto="1" hostip="127.0.0.1" hostport="6555" guestip="10.0.2.15" guestport="5555"/> </NAT> </Adapter> </Network> </Hardware> </Machine> </VirtualBox>
这里看到连接地址:hostip
, hostport
在 dos
下运行命令:
adb connect [hostip]:[hostport]
连接模拟器,到此 TYPE
下面命令就可以查询到模拟器了。
adb devices
3. MonkeyRunner 字符编码问题
dos
命令 chcp
的介绍和使用,点击这里
显示当前 dos
环境下的编码格式
> chcp
切换到简体中文编码格式
> chcp 936
切换到英文编码格式
> chcp 437
切换到 utf-8
编码格式
> chcp 65001
如果在 monkeyrunner
中打印内容,报如下错误:
LookupError: unknown encoding 'ms936'
这个就是编码问题,执行上面的 chcp
命令,切换我们需要的编码。