【转】Android自动化测试之MonkeyRunner MonkeyDevice MonkeyImage API使用详解 脚本编写 脚本录制回放

【内容转自】:https://blog.csdn.net/qq_30993595/article/details/80872808

MonkeyRunner简介:

monkeyrunner工具提供了一个API,用于编写从Android代码之外控制Android设备或模拟器的程序。 使用monkeyrunner,你可以编写一个Python程序,安装Android应用程序或测试包,运行它,向其发送击键,截取其用户界面,并在工作站上存储屏幕截图。 monkeyrunner工具主要用于测试功能/框架级别的应用程序和设备以及运行单元测试套件,但您可以将其用于其他目的。

Monkeyrunner工具为Android测试提供了以下独特功能:

多设备控制:monkeyrunner API可以在多个设备或仿真器上应用一个或多个测试套件。您可以物理连接所有设备或一次启动所有模拟器(或两者),以编程方式依次连接每个模拟器,然后运行一个或多个测试。您也可以以编程方式启动仿真器配置,运行一个或多个测试,然后关闭仿真器。

功能测试:monkeyrunner可以运行Android应用程序的自动化开始至结束测试。您可以使用按键或触摸事件提供输入值,并将结果视为截图。

回归测试: monkeyrunner可以通过运行应用程序并将其输出截图与一组已知正确的截图进行比较来测试应用程序的稳定性。

可扩展的自动化:由于monkeyrunner是一个API工具包,因此您可以开发一整套基于Python的模块和程序来控制Android设备。除了使用monkeyrunner API本身,您可以使用标准的Python操作系统和子流程模块来调用Android工具,如Android调试桥。您也可以将您自己的类添加到monkeyrunner API。这在扩展带有插件的monkeyrunner一节中有更详细的描述。

monkeyrunner工具使用Jython,这是一种使用Java编程语言的Python实现。 Jython允许monkeyrunner API与Android框架轻松交互。使用Jython,您可以使用Python语法来访问API的常量,类和方法。

1)Monkey是通过adb shell 命令发送随机命令,主要用来做压力测试;而MonkeyRunner是Andoroid SDK自带的一个黑盒测试工具,在PC端可以通过API来产生特定的命令或事件控制设备,可以实现Monkey无法实现的一些逻辑控制。
2)MonkeyRunner是一类api集合,我们可以使用这些api实现我们想要的效果
3)虽然脚本使用 Python 语法编写,但它实际上是通过 Jython 来解释执行。 Jython 是 Python 的 Java 实现,它将 Python 代码解释成 Java 虚拟机上的字节码并执行,这种做法允许在 Python 中继承一个 Java 类型,可以调用任意的 Java API

MonkeyRunner 三大模块

MonkeyRunner:这个类提供了一个将monkeyrunner连接到设备或模拟器的方法。它还提供了为monkeyrunner程序创建用户界面和显示内置帮助的方法
MonkeyDevice:表示设备或者模拟器,该类提供了安装和卸载软件包,启动Activity以及将键盘或触摸事件发送给应用程序的方法。你也可以使用这个类来运行测试包
MonkeyImage:该类提供了捕获屏幕,将位图图像转换为各种格式,比较两个MonkeyImage对象以及将图像写入文件的方法

我们可以在Python程序中使用 from com.android.monkeyrunner import 来导入这些类

PS:在pycharm中可以编写对应的py脚本,但不可在pycharm中运行,报错:ImportError: No module named com.android.monkeyrunner

运行py脚本,window系统下DOS环境:monkeyrunner   xx.py

MonkeyRunner API

alert:在当前正在运行的程序弹出一个警告框
choice:在当前正在运行的程序显示一个带列表选择的弹出框
help:使用指定的格式以类似于Python的pydoc工具的样式显示monkeyrunner API引用
input:显示一个弹出框接收输入
sleep:暂停当前进程
waitForConnection:尝试在monkeyrunner后端与指定的设备或模拟器之间建立连接

MonkeyDevice API

 

broadcastIntent: 向此设备发送一个广播
drag:模拟该设备屏幕上的拖动手势(触摸、按住和移动)
getProperty:根据给定系统环境变量的名称,返回该设备的值
installPackage:安装应用,如果已经安装则替换
instrument:进行单元测试的功能,后面再讲
press:发送按键事件
reboot:重启设备
removePackage:卸载应用,包括删除数据和缓存
shell:执行adb shell命令
startActivity :打开一个Activity
takeSnapshot :对设备当前界面截图,并返回一个MonkeyImage对象
touch:一个单击的手势操作
type:输入一个字符串
wake :唤醒屏幕

 MonkeyImage API

convertToBytes:将当前图像转换为字节码数据
getRawPixel :以(a,r,g,b)的形式返回图像指定位置(x,y)的单个像素,作为整数 tuple(Python中的集合)
getRawPixelInt:返回图像位置(x,y)的单个像素,作为整数。用这种方法可以节省内存
getSubImage :根据给定的矩形区域在当前屏幕创建一个新的MonkeyImage对象,由起点的xy坐标加上宽高决定一个区域
sameAs :将两个MonkeyImage对象进行比较,并返回比较的结果,percent参数指定允许两个图像“相等”的百分比差异,值是从0到1
writeToFile :将当前图像以指定的格式写入指定的文件,这个格式一般是png

dos窗口方法使用

使用这些api可以直接在dos中操作,也可以直接在PyCharm中编写python脚本,这个跟使用python一样的,这里先从dos窗口里操作讲解,其中的代码写法跟Python的写法一样的

直接输入monkeyrunner命令

不过你有可能得到这样的错误

SWT folder '..\framework\x86_64' does not exist.

Please set ANDROID_SWT to point to the folder containing swt.jar for your platform

这时候需要修改下monkeyrunner.bat内容:

set frameworkdir=lib --》set frameworkdir=…\lib
Dcom.android.monkeyrunner.bindir=…\framework -jar %jarpath% %* —》Dcom.android.monkeyrunner.bindir=…\platform-tools -jar %jarpath% %*

使用这些模块前需要导入上面说的几个模块,命令如下

from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice,MonkeyImage

先使用下MonkeyRunner的命令,比如查看帮助,使用help命令,接收一个参数,表示输出格式,有两种选择:“text”、“html”;这里我选择text

可以看到报错了,这是因为SDK集成monkeyrunner工具时没有将这些资源一起集成进来,所以需要我们将monkeyrunner源码中的resources目录拷贝到SDK的tools\lib目录,接下来你就能看到如下内容

 这样可读性比较差,我们可以将这些内容保存到文件中,进行如下操作

>>> content = MonkeyRunner.help("html")
>>> f = open("help.html","w")
>>> f.write(content)
>>> f.close()
>>>

第一句是定义一个变量content,接收html的内容
第二句是定义一个文件变量f,并创建一个可写入的文件
第三句是将content写入到f中
第四句是关闭写入操作

这些是常用的Python语法;这里没有指定文件目录,而monkeyrunner命令工具是在tools\bin目录,所以这个文件默认保存在这里

该文件里的内容就是对所有api进行说明
MonkeyRunner 方法使用

接下来使用alert方法

MonkeyRunner.alert('MonkeyRunner alert','mango','ok')

使用choice方法

MonkeyRunner.choice('MonkeyRunner choice',['a','b','c'],'mango')

同时该方法有一个返回值,是你选择项的index值,比如我选a,那么就会返回0

还有一个弹出框方法input

MonkeyRunner.input('MonkeyRunner input','input','mango','ok','cancle')

点击确定后

返回值就是你输入的值

接下来一个方法sleep,就是中断脚本执行一段时间

最后一个方法很重要,有两个参数,第一个是超时时间,第二个是设备id,如果不指定,就默认连接adb已经连接上的设备;通过该方法可以跟设备建立连接

可以看到它返回了一个MonkeyDevice对象
MonkeyDevice 方法使用

这个模块是MonkeyRunner中的一个核心模块,看名字就知道,它负责和设备的一个交互,控制设备执行相应的操作:包括安装应用、启动应用、进行点击操作、对设备截图、获取设备上的一些系统变量等功能;接下来看下这些方法的具体使用

惯例先导入这些模块

from com.android.monkeyrunner import MonkeyRunner as mr
from com.android.monkeyrunner import MonkeyDevice as md
from com.android.monkeyrunner import MonkeyImage as mi

其中的as意思是起别名,要不然后面每次使用这些模块都用MonkeyRunner这些名词,有点太麻烦了

第一步就是与设备建立连接:

>>> device = mr.waitForConnection()

第二步就是安装应用

>>> device.installPackage("G:\Android Video\TestActivity.apk")
>True

返回值是true说明应用安装成功

第三步要启动应用,但是先查看下应用包名,可以通过如下命令

>>> device.shell("pm list package |grep testing")
u'package:com.example.android.testingfun\n'

再通过如下命令启动应用

>>> package = "com.example.android.testingfun"
>>> activity = "com.example.android.testingfun.ClickFunActivity"
>>> mCompontent = package + "/" + activity
>>> device.startActivity(component=mCompontent)

接下来可以发送一个按键事件

按下HOME键 device.press('KEYCODE_HOME',md.DOWN_AND_UP)
按下BACK键 device.press('KEYCODE_BACK',md.DOWN_AND_UP)

一些常用的按键有

Home键:KEYCOD_HOME
Back键:KEYCODE_BACK
send键:KEYCODE_CALL
end键:KEYCODE_ENDCALL
上导航键:KEYCODE_DPAD_UP(现在手机已经没有这个键)
下导航键:KEYCODE_DPAD_DOWN(现在手机已经没有这个键)
左导航:KEYCODE_DPAD_LEFT 现在手机已经没有这个键
右导航键:KEYCODE_DPAD_RIGHT 现在手机已经没有这个键
ok键:KEYCODE_DPAD_CENTER
上音量键:KEYCODE_VOLUME_UP
下音量键:KEYCODE_VOLUME_DOWN
power键:KEYCODE_POWER
camera键:KEYCODE_CAMERA
menu键:KEYCODE_MENU
search键:KEYCODE_SEARCH
call键:KEYCODE_CALL

第四步我们试下touch方法,点击屏幕,但是它需要接受坐标,所以我们需要知道要点击的view的具体坐标,这时候需要使用uiautomatorviewer工具,直接打开一个新的dos窗口输入uiautomatorviewer,就可以在弹出的界面中选中想要点击的view,看看它所在的范围;也可以打开设备的USB调试模式中,选中显示坐标选项,这时点击屏幕,顶部会显示对应的坐标点

我这里是点中一个输入框,接下来可以使用type方法输入字符串到输入框

>>> device.touch(300,900,md.DOWN_AND_UP)
>>> device.type('s')

要知道一般点击输入框的时候都会将系统输入法调出来,所以接下来需要将输入框隐藏,发送一个返回键事件

>>> device.press('KEYCODE_BACK',md.DOWN_AND_UP)

接下来就可以继续点击其它View了

接下来还可以通过getProperty获取设备的一些参数信息,但是该方法接收一个参数,就是属性名,可以通过如下方法获取系统所有的属性名

>>> device.getPropertyList()

>>> device.getProperty('display.width')
u'1080'
>>> device.getProperty('display.height')
u'1920'
>>> device.getProperty('display.density')
u'3.0'
>>>device.shell('dumpsys battery|grep level')
u'level: 90\n'

MonkeyDevice还有一个非常重要的方法在官网没有写出来,就是getHierarchyViewer(),返回一个View层级对象,可以使用它根据view的id找到对应的View及其它一些操作

viewer = device.getHierarchyViewer()
note = viewer.findViewById('id/title')
text = viewer.getText(note)
print text.encode('utf-8')

point = viewer.getAbsoluteCenterOfView(note)
x = point.x
y = point.y

MonkeyImage 方法使用

这也是monkeyrunner中一个比较重要的模块,它是一个图像处理的模块,主要是对截屏生成的图片进行一些操作,具体哪些操作在上面介绍过了,具体使用如下

首先需要得到一个MonkeyImage对象

>>> image = device.takeSnapshot()

使用convertToBytes (string format)方法

返回字节码数据,默认是png格式,还有其它参数

image.convertToBytes('jpg')
image.convertToBytes('gif')

获取指定坐标的像素值

>>> image.getRawPixel(0,0)
(-1, 48, 63, 159)
>>> image.getRawPixelInt(0,0)
-13615201

两个方法都是获得指定一个坐标的argb值,但前者返回的是一个元组,后者返回的是整型。那么两个类型的值是怎么相应起来的呢?事实上就是第一个方法的元组的返回值(a,r,g,b)中的每一个值转换成8个bit的二进制值然后按顺序从左到右排列起来再转换成十进制整型就是第二个方法的返回值了。

接下来看看获取子图像的方法

>>> newImage = image.getSubImage((0,0,720,1280))
>>> print (newImage.sameAs(image,1.0))
False

sameAs的第二个参数:1.0代表必须100%同样,0.5代表能够有50%的Error Tolerance.

最后就是保存图像了

>>> newImage.writeToFile('v.png','png')
True

脚本方法使用

#导入我们需要用到的包和类并且起别名

#!/usr/bin/python 2.7
# -*- coding: UTF-8 -*-

"""
@projectname:pycharmTest
@filename:monkeyrunner-1.py
@author:adminshw 
@time:2020-3-25 11:29
@software:PyCharm
"""

#导入我们需要用到的包和类并且起别名
import sys
from com.android.monkeyrunner import MonkeyRunner as mr
from com.android.monkeyrunner import MonkeyDevice as md
from com.android.monkeyrunner import MonkeyImage as mi

# https://blog.csdn.net/qq_30993595/article/details/80872808
# 连接设备
#第一个参数为等待连接设备时间
#第二个参数为具体连接的设备
device = mr.waitForConnection()
if not device:
    print >> sys.stderr,"fail"
    sys.exit(1)
# 安装APP installPackage 会返回一个布尔值,来说明安装的结果
# apkFilePath = "D:/monkey.apk";
# device.installPackage(apkFilePath);

# 启动 APP
package = "com.qxzn.desktopxiaopairobot"
activity = "com.qxzn.desktopxiaopairobot.mainpage.SplashActivity"
mCompontent = package + "/" + activity
device.startActivity(component=mCompontent)
# 这里一般让脚本暂停一段时间,使得APP成功启动起来
mr.sleep(8.0)
for i in range(1,2):
    print '--------------'
    print ''+str(i)+'次执行'
    # 获取当前电量
    strs = device.shell('dumpsys battery|grep level').split(':')[1].strip()
    print '机器人当前电量'+ str(strs)
    print '--------------'
    # 电量小于20时,停止执行脚本
    if int(strs) < 20:
        sys.exit(1)
    # 下面开始模拟界面事件
    # 1. 点击健康档案应用
    device.touch(845,388, "DOWN_AND_UP")
    print '等待6s'
    # 等待6秒,进入人脸识别失败页面
    mr.sleep(8.0)
    # 2. 输入事件
    # 点击:验证安全码,进入选择用户验证安全码页面
    device.touch(1280,1153, md.DOWN_AND_UP)
    mr.sleep(3.0)
    # 选择用户
    device.touch(300,620, md.DOWN_AND_UP)
    mr.sleep(2.0)
    # 进入输入安全码页面
    # device.touch(564,734,md.DOWN_AND_UP)
    device.type("123654")
    # 进入用户健康档案页面
    mr.sleep(3.0)
    # 选择血压指标
    device.touch(1634,334,md.DOWN_AND_UP)
    mr.sleep(2.0)
    # 返回健康档案页面
    device.press("KEYCODE_BACK",md.DOWN_AND_UP)
    mr.sleep(2.0)
    # 返回健康主页面
    print '返回健康主页面'
    device.press("KEYCODE_BACK", md.DOWN_AND_UP)
    mr.sleep(5.0)
print '共执行'+str(i)+''    
    # 4. 界面滑动
    # device.drag((x1, y1), (x2, y2))
    # mr.sleep(1)
    # # 点击事件触发,界面开始变化,截屏比较
    # imagetobecompared = MonkeyRunner.loadImageFromFile([image file path])
    # screenshot = device.takeSnapshot()
    # if screenshot.sameAs(imagetobecompared, 0.9):
    #   print "2张图片相同"
    # else:
    #   print "2张图片不相同"
    # # 保存截图
    # result.writeToFile('./shot1.png','png')
    # #卸载app
    # device.removePackage(package)

从上面的实例中我们可以看出使用monkeyrunner测试应用程序的具体步骤为:

1、导入需要的包

2、连接设备,等待设备连接并返回连接的设备

3、安装测试程序包(可写绝对路径)

4、设置启动程序包名和Activity名

5、执行一系列的touch、drag等事件

6、截图保存

7、卸载APP
执行monkeyRunner脚本

打开dos窗口,进入sdk目录的tools目录,有的可能在tools目录下的次级目录bin目录下,反正是monkeyrunner.bat的目录
输入命令 monkeyrunner monkey.py 然后敲回车执行命令

MonkeyRunner脚本录制

这个功能是真的有用,不需要你磨磨唧唧写脚本了,你直接按测试用例对app操作一遍,然后把这些操作录制成脚本,以后就跑这个脚本就可以了。只不过这玩意官网上也没提,要想了解怎么使用需要去看源码,在monkeyrunner\scripts目录下有这么几个文件

其中monkey_recorder.py是录制程序,monkey_playback.py是回放程序,要想方便使用这两个Python程序,需要将它们放到与monkeyrunner脚本所在的目录,脚本很简单,如下

#!/usr/bin/env monkeyrunner
# Copyright 2010, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from com.android.monkeyrunner import MonkeyRunner as mr
from com.android.monkeyrunner.recorder import MonkeyRecorder as recorder

device = mr.waitForConnection()
recorder.start(device)

然后输入如下命令即可执行该脚本

>>>monkeyrunner monkey_recorder.py

弹出框如下

顶部栏按钮简介:

Wait 设置执行下一条命令的等待时间
Press a Button 发送MENU HOME SEARCH按钮的Press Down Up事件
Type Something 输入并发送字符串到设备
Fling 用东南西北四个方向的虚拟键盘来滑动屏幕
Export Actions 导出操作录制脚本
Refresh Display 刷新屏幕

接下来我们就可以打开需要测试的app,然后按照用例点击测试,右边空白部分就会显示我们点击操作一番操作后如下图

 最后导出脚本为test.mr并保存,脚本文件可以打开修改

TOUCH|{'x':546,'y':300,'type':'downAndUp',}
WAIT|{'seconds':1.0,}
PRESS|{'name':'BACK','type':'downAndUp',}
TOUCH|{'x':546,'y':440,'type':'downAndUp',}
WAIT|{'seconds':1.0,}
TOUCH|{'x':519,'y':1088,'type':'downAndUp',}
WAIT|{'seconds':1.0,}
TOUCH|{'x':550,'y':584,'type':'downAndUp',}
WAIT|{'seconds':1.0,}
TOUCH|{'x':553,'y':1824,'type':'downAndUp',}
WAIT|{'seconds':1.0,}
TOUCH|{'x':540,'y':736,'type':'downAndUp',}
WAIT|{'seconds':2.0,}
TOUCH|{'x':590,'y':1804,'type':'downAndUp',}
WAIT|{'seconds':2.0,}
PRESS|{'name':'MENU','type':'downAndUp',}
PRESS|{'name':'HOME','type':'downAndUp',}

脚本内容跟前面讲的MonkeyRunner三大模块的api不一样,因为这是录制工具自定义的一套api

MonkeyRunner脚本回放

monkey_playback.py脚本如下:

# !/usr/bin/env monkeyrunner
# Copyright 2010, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
from com.android.monkeyrunner import MonkeyRunner

# The format of the file we are parsing is very carfeully constructed.
# Each line corresponds to a single command.  The line is split into 2
# parts with a | character.  Text to the left of the pipe denotes
# which command to run.  The text to the right of the pipe is a python
# dictionary (it can be evaled into existence) that specifies the
# arguments for the command.  In most cases, this directly maps to the
# keyword argument dictionary that could be passed to the underlying
# command.
# Lookup table to map command strings to functions that implement that
# command.

CMD_MAP = {
    'TOUCH': lambda dev, arg: dev.touch(**arg),
    'DRAG': lambda dev, arg: dev.drag(**arg),
    'PRESS': lambda dev, arg: dev.press(**arg),
    'TYPE': lambda dev, arg: dev.type(**arg),
    'WAIT': lambda dev, arg: MonkeyRunner.sleep(**arg)
}
# Process a single file for the specified device.
def process_file(fp, device):
    for line in fp:
        (cmd, rest) = line.split('|')
        try:
            # Parse the pydict
            rest = eval(rest)
        except:
            print 'unable to parse options'
            continue
        if cmd not in CMD_MAP:
            print 'unknown command: ' + cmd
            continue
        CMD_MAP[cmd](device, rest)

def main():
    file = sys.argv[1]
    fp = open(file, 'r')
    device = MonkeyRunner.waitForConnection()
    process_file(fp, device)
    fp.close();


if __name__ == '__main__':
    main()

这个就是将上面录制的脚本进行回放,也就是让设备自动按照录制的脚本进行自动化测试,这里以上面录制的脚本为例,输入如下命令

monkeyrunner monkey_playback.py D:\AndroidSDK\tools\bin\test.mr

其中第二个参数是脚本路径,注意一定要是绝对路径


EasyMonkeyDevice、By

通过前面的讲解,可以知道MonkeyRunner进行自动化测试主要是通过touch方法,传入的参数是坐标,这样就带来了一个很严重的问题,Android厂家这么多,各种分辨率的屏幕数不胜数,你在这块屏幕上写的坐标在另一块屏幕上并不能起作用,那基于坐标的同一份脚本就不能通用,那这是不是就意味着GG了呢?

不是的,monkeyrunner还有两个模块就是easymonkeydevice和by,可以实现基于控件的自动化测试脚本,但是这两个模块在官方文档也没有任何的资料,所以我们也只有去它的源码查看

在其源码的src\com\android\monkeyrunner\easy目录下有这两个文件

里面有个README文件,打开看看

开头就说了easy模块包含了一些更容易使用MonkeyRunner的代码,使用控件的id代替了xy坐标去找到控件;下面也给出了使用样例

其中By模块主要提供了一个id(String id)方法,返回By对象;而EasyMonkeyDevice模块主要提供了这么几个方法

EasyMonkeyDevice(MonkeyDevice device):构造方法,参数是MonkeyDevice
touch(By selector, TouchPressType type):第一个参数是包含了控件id的By对象,第二个参数是触屏操作类型
type(By selector, String text):第一个参数是包含了控件id的By对象,第二个参数输入的字符串
visible(By selector):控件是否显示
getText(By selector) :获取控件文本值
getElementCenter(By selector):获取某个控件的中心点坐标
exists(By selector):判断某个控件是否存在
getFocusedWindowId():获取一个具有当前窗口焦点的控件id

接下来看看具体的使用

使用前需要导入这几个模块,如下

>>> from com.android.monkeyrunner import MonkeyRunner as mr,MonkeyDevice as md,MonkeyImage as mi
>>> from com.android.monkeyrunner.easy import EasyMonkeyDevice as emd,By

然后连接设备启动应用

>>> device = mr.waitForConnection()
>>> device.startActivity('com.android.mangodialog/.MainActivity')

接下来就需要获取EasyMonkeyDevice对象了,通过构造方法获取

easy = emd(device)

但是你可能会遇到下面的错误

java.lang.RuntimeException: Could not connect to the view server

这是因为我们的设备没有启动view server功能,同时这个功能一般只在模拟器或者开发版本的设备上才会提供,一般商用版本需要root才能使用

在模拟器上启动view server命令如下

adb shell service call window 1 i32 4939

如果返回值是

Result: Parcel(00000000 00000001 '........')

说明启动成功,查看view server状态命令是adb shell service call window 3

然后就可以通过如下命令进行点击操作

easy.touch(By.id('id/clear'),md.DOWN_AND_UP)

HierarchyViewer

可以通过HierarchyViewer模块来解析控件ID,获取对应的View,它支持的方法有

findViewById(String id):根据id找到控件
getAbsolutePositionOfView(ViewNode node):返回view的位置,指的是left,top值
getAbsoluteCenterOfView(ViewNode node):获取view中心点坐标
getText(ViewNode node):获取view的文本值

通常使用device.getHierarchyViewer()方法获取该对象,常用方法如下

content = hViewer.findViewById('id/text') # 通过id查找对应元素返回viewnode对象来访问属性
value = hViewer.getText(content)

但是会出现跟EasyMonkeyDevice同样的问题,需要启动view service
脚本样例

下面是一个计算器的自动化测试脚本

#导入我们需要用到的包和类并且起别名
import sys
from com.android.monkeyrunner import MonkeyRunner as mr
from com.android.monkeyrunner import MonkeyDevice as md
from com.android.monkeyrunner import MonkeyImage as mi
from com.android.chimpchat.hierarchyviewer import HierarchyViewer #根据ID找到ViewNode,对viewnode的一些操作等
from com.android.monkeyrunner.easy import EasyMonkeyDevice  #提供了根据ID进行访问方法touch、drag等
from com.android.monkeyrunner.easy import By    #根据ID返回PyObject的方法
from com.android.hierarchyviewerlib.models import ViewNode as vn #代表一个控件,可获取控件属性


#connect device 连接设备
#第一个参数为等待连接设备时间
#第二个参数为具体连接的设备
device = mr.waitForConnection(1.0,'34ca2501')
if not device:
    print >> sys.stderr,"fail"
    sys.exit(1)

#定义要启动的Activity
componentName="com.android.mangodialog/.MainActivity"

#启动特定的Activity
device.startActivity(component=componentName)
mr.sleep(5.0)#延时时间结合自身机器环境需要调整

easy_device = EasyMonkeyDevice(device)#初始化EasyMonkeyDevice模块,必须放在startActivity之后,用来通过ID访问控件
hViewer = device.getHierarchyViewer() # 对当前UI视图进行解析

#执行1到9的累加操作
#1、通过坐标方式来获取
device.touch(93,241,device.DOWN_AND_UP)    #1
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)   #+
mr.sleep(2.0)
device.touch(249,235,device.DOWN_AND_UP)    #2
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)     #+
mr.sleep(2.0)
device.touch(370,231,device.DOWN_AND_UP)    #3
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)     #+
mr.sleep(2.0)
device.touch(106,315,device.DOWN_AND_UP)    #4
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)   #+
mr.sleep(2.0)
device.touch(253,323,device.DOWN_AND_UP)     #5
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)    #+
mr.sleep(2.0)
device.touch(397,328,device.DOWN_AND_UP)     #6
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)    #+
mr.sleep(2.0)
device.touch(96,411,device.DOWN_AND_UP)     #7
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)    #+
mr.sleep(2.0)
device.touch(270,406,device.DOWN_AND_UP)     #8
mr.sleep(2.0)
device.touch(238,490,device.DOWN_AND_UP)    #+
mr.sleep(2.0)
device.touch(402,423,device.DOWN_AND_UP)     #9
mr.sleep(2.0)
device.touch(387,670,device.DOWN_AND_UP)    #=
mr.sleep(2.0)

#takeSnapshot截图,获取程序运行界面截图
result0 = device.takeSnapshot()
#save to file 保存到文件
result0.writeToFile('./shot1.png','png');

#2、通过控件ID来获取
easy_device.touch(By.id('id/clear'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/btn1'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/tv'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/btn2'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/view'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/btn3'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/img'),device.DOWN_AND_UP)
easy_device.touch(By.id('id/btn4'),device.DOWN_AND_UP)
mr.sleep(3.0)
#takeSnapshot截图,获取程序运行界面截图
result1 = device.takeSnapshot()

#save to file 保存到文件
result1.writeToFile('./shot2.png','png');
if(result1.sameAs(result0,1.0)):#截图对比
    print("pic true")
else:
    print("pic false") #全图100%对比 因为时间不同会输出false

#对比局部图片(去掉状态栏,因为状态栏时间会改变)
pic0= result0.getSubImage((4,41,400,700)) #局部结果图形对比
pic1= result1.getSubImage((4,41,400,700))
print (pic1.sameAs(pic0,1.0)) #输出true


#通过HierarchyViewer
content = hViewer.findViewById('id/text')  # 通过id查找对应元素返回viewnode对象来访问属性
text0 = hViewer.getText(content)
print text0.encode('utf-8')#打印结果

#通过By来获取
text1=easy_device.getText(By.id('id/text'))
print text1.encode('utf-8')#打印结果

device.press('KEYCODE_BACK', device.DOWN_AND_UP)

Bug回归验证

意思就是修改bug后再进行验证是否修改正确,monkeyrunner中是通过比对两次截图来判断是否正确修改bug

#从本地加载shot1-1.png,上一次的截图
result0 = mr.loadImageFromFile('./shot1-1.png')
#对比局部图片
pic0= result0.getSubImage((4,41,400,700)) #局部结果图形对比
pic1= result1.getSubImage((4,41,400,700))
print (pic1.sameAs(pic0,1.0)) #输出true就是bug已经修改
posted @ 2020-03-28 15:01  Monogem  阅读(803)  评论(0编辑  收藏  举报