移动端自动化测试编程Android

移动端自动化测试----MonkeyRunner

2018-01-23  本文已影响78人  我为峰2014

安装

安装并且配置好JDK和SDK环境变量,同时安装好Python并且配置好环境变量

monkeyrunner.bat文件所在位置\sdk\tools\bin
即在SDK的tools\bin目录下面,将此路径写在path里面

启动错误

SWT folder '..\framework\x86' does not exist.
Please set ANDROID_SWT to point to the folder containing swt.jar for your platform.

打开monkeyrunner.bat文件

把framework改为bin

image.png

然后执行monkeyrunner会发生adb找不到的错误

解决办法:

在tools下新建framework文件夹,复制adb.exe到此文件夹下,并且把bin文件夹里面的全部内容也复制过去

再次运行monkenrunner就可以运行了。

image.png

Monkeyrunner使用方法

Moneyrunner在使用前,必须先打开模拟器或连接上手机设备。

1,模拟器启动

我们这里选择使用命令打开模拟器。
运行cmd,在cmd窗口,输入命令:emulator -avd AVD_test
其中AVD_test是模拟器的名称,填写自己创建的模拟器名称。

image.png

当然你也可以不使用命令模式,可以打开Android Studio中打开模拟器

2,真机启动

使用adb连接好真机,使用adb devices命令查看连接的设备

Monkeyrunner连接模拟器或者真机

引入monkeyrunner需要的两个模块

from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice 

输入命令:

device=MonkeyRunner.waitForConnection()

其中,device=MonkeyRunner.waitForConnection(5,’Z2X4C15528000215′)

参数1:超时时间,单位秒,浮点数,默认是无限期地等待。
参数2:指定的设备名称device_id,默认为当前设备(手机优先,其次为模拟器)

image.png

没有报错,就说明已经连接上了

MonkeyRunner 工具

MonkeyRunner 工具提供了在 Android 代码外编写控制 Android 设备或虚拟机程序的 API。

MonkeyRunner 工具 通过发送 API 中明确的指令或事件,在工作目录控制设备或模拟器。

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

你也可以向 MonkeyRunner API 中添加你自己的类。

MonkeyRunner工具使用 Jython,一个使用Java语言实现的 Python。Jython 允许 MonkeyRunner API 方便的与 Android 框架层交互。在 Jythong 中,你可以使用 Python 语法访问 API 中的常量,类,和方法等。

MonkeyRunner API

com.android.monkeyrunner包中有 三个 monkeyrunner API :

在一个 Python 程序中,你可以将每个类都当作一个 Python 模块访问。monkeyrunner 工具不会自动导入这些模块,你可以使用 Python 的import语句导入模块:

from com.android.monkeyrunner import <module>

<module> 代表了一个你想要导入的类名。 你可以在一个from语句中导入多个模块,模块之间用逗号分割。

运行MonkeyRunner

命令语法为:monkeyrunner

方式一:在CMD命令窗口直接运行monkeyrunner,然后输入命令

方式二:使用Python编写测试代码文件,在CMD中cd测试代码文件目录来执行monkeyrunner test.py运行

不论使用哪种方式,您都需要调用SDK目录的tools子目录下的monkeyrunner命令。

注意:在运行monkeyrunner之前必须先运行相应的模拟器或连接真机,否则monkeyrunner无法连接到设备

官方API

MonkeyRunner

MonkeyRunner类,包含一下静态方法

void alert(string message,string title,string okTitle)

向运行当前程序的进程显示警告对话框。弹出一个确认框,在pc上面

integer choice(string message,iterable choices,string title)

显示一个对话框,其中包含运行当前程序的进程的选项列表。对话框是模态的,所以程序暂停,直到用户单击其中一个对话框的按钮。

void help(string format)

以类似于Python的pydoc工具的风格,使用指定的格式显示monkeyrunner API引用。

string input(string message,string initialValue,string title,string okTitle,string cancelTitle)

显示接受输入并将其返回给程序的对话框。对话框是模态的,所以程序暂停,直到用户单击其中一个对话框的按钮。 该对话框包含两个按钮,其中一个显示okTitle值,另一个显示为cancelTitle值。如果用户单击okTitle按钮,则返回输入框的当前值。如果用户点击cancelTitle按钮,则返回一个空字符串。

void sleep(float seconds)

暂停当前程序指定的秒数。

MonkeyDevice [waitForConnection])(float timeout,string deviceId)

尝试在monkeyrunner后端和指定的设备或模拟器之间建立连接。

MonkeyDevice

Monkeyrunner的工作站中可访问的设备或仿真器。此类用于控制Android设备或模拟器。方法发送UI事件,检索信息,安装和删除应用程序,并运行应用程序。

通常不需要创建一个MonkeyDevice实例。相反,您可以使用MonkeyRunner.waitForConnection()从与设备或模拟器的连接创建新对象。

例如,

不使用:

newdevice=MonkeyDevice()

需要用这种方式获取Device对象:

newdevice=MonkeyRunner.waitForConnection()

常量

string DOWN使用press()或touch()的type参数来发送DOWN事件。

string UP使用press()或touch()的type参数来发送UP事件。

string DOWN_AND_UP使用press()或touch()的type参数立即发送一个DOWN事件,然后再发送UP事件。

方法

void broadcastIntent(*string uri,string action,string data,string mimetype,iterable categories dictionary extras,component component,iterable flags)

广播此设备的意图,就像Intent来自应用程序一样。有关参数的更多信息,请参阅Intent。

void drag(tuple start,tuple end,float duration,integer steps)

在此设备的屏幕上模拟拖动手势(触摸,按住和移动)。

object getProperty(string key)

给定系统环境变量的名称,返回该设备的值。可用的变量名称在下面表1中列出

object getSystemProperty(string key)

.adb shell的API相当于getprop 。这是供平台开发人员使用的。

void installPackage(string path)

将packageFile中包含的Android应用程序或测试包安装到此设备上。如果应用程序或测试包已经安装,它将被替换。

dictionary instrument(string className,dictionary args)

在Android测试下运行指定的组件,并将结果返回到字典中,其精确格式由正在运行的组件决定。组件必须已经存在于此设备上。

void press(string name,dictionary type)

将由type指定的键事件发送到由keycode指定的键。

void reboot(string into)

将此设备重新引导到由bootloadType指定的引导加载程序中。

void removePackage(string package)

从该设备中删除指定的包,包括其数据和缓存。

object shell(string cmd)

执行一个adb shell命令并返回结果(如果有的话)。

void startActivity(string uri,string action,string data,string mimetype,iterable categories dictionary extras,component component,flags)

通过发送从提供的参数构造的Intent来启动此设备上的活动。

MonkeyImage takeSnapshot()

捕获此设备的整个屏幕缓冲区,产生一个包含当前显示屏幕捕获的MonkeyImage对象。

void touch(integer x,integer y,integer type)

将类型指定的触摸事件发送到由x和y指定的屏幕位置。

void type(string message)

将消息中包含的字符发送到此设备,就像在设备的键盘上键入一样。这相当于使用键事件类型DOWN_AND_UP为消息中的每个键码调用press()。

void wake()

唤醒此设备的屏幕。

MonkeyImage

一个Monkeyrunner类来保存设备或模拟器屏幕的图像。在屏幕截图期间,图像从屏幕缓冲区复制。该对象的方法允许您将图像转换为各种存储格式,将图像写入文件,复制图像部分,并将此对象与其他MonkeyImage对象进行比较。

您不需要创建MonkeyImage的新实例。相反,使用MonkeyDevice.takeSnapshot()从屏幕截图创建一个新的实例。

例如,使用:

newimage=MonkeyDevice.takeSnapshot()

方法

string convertToBytes(string format)

将当前图像转换为特定格式,并将其作为字符串返回,然后您可以作为二进制字节的迭代访问。

tuple getRawPixel(integer x,integer y)

以(a,r,g,b)的形式返回图像位置(x,y)上的单个像素作为整数元组。

integer getRawPixelInt(integer x,integer y)

将图像位置(x,y)上的单个像素作为32位整数返回。

MonkeyImage getSubImage(tuple rect)

从当前图像的矩形选择中创建一个新的MonkeyImage对象。

boolean sameAs(MonkeyImage other,float percent)

将此MonkeyImage对象与另一个对象进行比较,并返回比较结果。百分比参数指定允许两个图像“相等”的百分比差异。

void writeToFile(string path,string format)

将当前图像写入由文件名指定的文件,格式为格式指定。

一个简单的例子

#coding=utf-8
# 引入本程序所用到的模块
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage

# 连接手机设备
device = MonkeyRunner.waitForConnection()

# 截图
result = device.takeSnapshot()

# 将截图保存到文件 
result.writeToFile('./shot01.png','png')

# 卸载APP
device.removePackage('<package name>')
print ('remove   Success')

# 暂停5秒
MonkeyRunner.sleep(5)

# 截图
result = device.takeSnapshot()
result.writeToFile('./shot02.png','png')

# 安装新的APP
device.installPackage('apk的绝对路径')
print ('安装成功')

# 截图
result = device.takeSnapshot()
print ('install Success')


#启动APK
device.startActivity(component = '<package name>/.activity') 

启动APK我们需要获取这个APK的<package name>和需要确定的activity,

可以通过adb 命令:adb logcat | findstr Displayed

image.png

MonkeyRunner的坐标和控件ID的获取方法

MonkeyRunner是Google提供的一个基于坐标点的Android黑盒自动化测试工具。所以,要使用Monkeyrunner进行自动化测试,首先,要了解Monkeyrunner中获取坐标点的方式。

image.png

一、控件坐标获取

MonkeyRecorder获取坐标

MonkeyRecorder是一个比较好用的获取坐标的工具,它是用来获取真机或模拟器上坐标的工具,当我们点击真机或模拟器上的空间时,就能显示真机或模拟器上的点击点的坐标。

命令:

from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice

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

device=MonkeyRunner.waitForConnection()

recorder.start(device)

MonkeyRecorder正式启动。截图如下

image.png

MonkeyRecorder的使用

这里只是使用MonkeyRecorder来记录坐标,获取坐标的方式很简单。比如qq的登录界面,用鼠标点击“登录”按钮,右侧就会显示该按钮的坐标;同样,点击账号输入框或密码输入框,右侧同样会显示坐标。这个坐标就是我们需要获得的坐标。

image.png

同时,MonkeyRecorder中的界面是同模拟器页面保持一致的,在MonkeyRecorder中触发任一操作,模拟器上会有相应的触发。如果两者没有保持一致,则点击MonkeyRecorder右上角的Refresh Display即可刷新页面。

image.png

二、控件ID获取

通过控件ID实现自动化脚本的运行,就性能而言,会比控件坐标的实现差一些;但是对于不同分辨率的设备都通用,不需要动态变换坐标。控件ID的获取主要是通过HierarchyViewer。

HierarchyViewer的打开方式

Hierarchy Viewer是随AndroidSDK发布的工具,位置在tools文件夹下,名为hierarchyviewer.bat。它是Android自带的非常有用而且使用简单的工具,可以帮助我们更好地检视和设计用户界面(UI)

我查看了我自己的sdk发现并没有找到sdk/tools/hierarchyviewer.bat,这个东西,然后百度

The standalone version of hieararchyviewer is deprecated.
Please use Android Device Monitor (tools/monitor.bat) instead.

大概意思是说,单独版本的 hieararchyviewer 已经被弃用了。请使用 Android Device Monitor来代替。Android Device Monitor在tools目录下面找到monitor.bat即可。

为了紧跟时代潮流,就决定用Android Device Monitor了!

友情提示:APP先运行起来再使用Android Device Monitor

image.png

HierarchyViewer的打开方式在Android Studio中

image.png

打开Hierarchy Viewe
启动Android Device Monitor成功之后,在新的的窗口中点击切换视图图标,选择Hierarchy Viewe,如下图:

image.png

HierarchyViewer默认只能在非加密设备使用,例如工程机,工程平板或者模拟器。如果要在手机上使用HierarchyViewer,你需要在你的应用中添加一个开源库View Server
链接地址:https://github.com/romainguy/ViewServer

该问题的详细原因:为什么HierachyViewer无法连接真机调试

我自己的手机没有能开启view server,所以只能使用模拟器了。
我的Genymotion模拟器总是登入不进去,就只能用自带的模拟器了╮(╯_╰)╭ ,我的电脑太卡,有有兴趣可以自己百度下,有很多资料的,

别人的图

image.png

通过滚动鼠标,可以放大每个树节点;拖拽鼠标,移动树形结构布局。双击树节点可以展示单独的UI部分

对于列表、或者弹出框则无法直接通过点击ID操作成功,需要计算ID的坐标。

MonkeyRunner的录制和回放

录制与回放文件准备:

MonkeyRunner的源代码可以在android源代码里找到,MonkeyRunner源代码里有个scripts文件夹,里面有monkey_recorder.py和monkey_playback.py两个python文件,这两个文件就是用来录制和回放的~!将这两个文件放入到sdk/tool/bin目录下。若没有找到这两个文件,我们可以自制文件:将下面两段代码分别放入对应monkey_recorder.py和monkey_playback.py两个python文件:

录制脚本:monkey_recorder.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.

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

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

回放脚本: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()

录制操作:

MonkeyRecorder启动后,界面上显示的内容就是已连接的手机或模拟器当前屏幕的内容。

用鼠标点击这个界面,点击的坐标就会被记录下来,同时结合MonkeyRecorder界面顶部的按钮,即可进行录制。

该窗口的功能:
1、可以自动显示手机当前的界面
2、自动刷新手机的最新状态
3、点击手机界面即可对手机进行操作,同时会反应到真机,而且会在右侧插入操作脚本

image.png

Wait: 用来插入下一次操作的时间间隔,点击后即可设置时间,单位是秒

Press a Button:用来确定需要点击的按钮,包括menu、home、search,以及对按钮的press、down、up属性

Type Something:用来输入内容到输入框

Fling:用来进行拖动操作,可以向上、下、左、右,以及操作的范围

Export Actions:用来导出脚本

Refresh Display:用来刷新手机界面,估计只有在断开手机后,重新连接时才会用到

回放操作:

录制完成后,就可以进行回放,操作步骤如下:

1.点击Export Actions,导出我们录制时的操作,在本地生成一个文件,并根据需要命名:xxx.mr

2.将录制的文件(假如命名为test.mr),拷贝到和monkeyrunner同级目录下,即sdk/tools/bin

3.将回放的脚本monkey_playback.py,同样保存在sdk/tools下;

monkey_playback.py代码如下:

语法格式:monkeyrunner 回放脚本名 保存的脚本)

monkeyrunner monkey_playback.py  xxx.mr

PS: 录制后的脚本可以进行二次更改,而且每一步操作需要有时间间隔,以保证测试的正确性。(在此录制工具上本身的间隔需要长一点时间,否则脚本不容易被录制)

MonkeyRunner的测试结果判断

monkeyrunner的功能脚本编写完成之后,我们就需要对结果进行判断,判断结果是否为我们预期的结果值

方式一、MonkeyRunner截图对比

这是monkeyrunner非常有特色的一种方式,用于通过设备屏幕前后的对比来获取对执行结果的判断。使用.sameAs()对比截图,获得测试结果

#连接设备
device = MonkeyRunner.waitForConnection()
#当前页面截图
image = device.takeSnapshot()
#去文件中找到我们要对比的正确的图片(这里比如存储在F盘,命名为result.png),与该截图image进行对比
result = MonkeyRunner.loadImageFromFile('F:\\result.png')
#判断图片相识度是否是90%(这个比值根据需求来定)
if image.sameAs(result,0.9):
    print "图片对比成功"
else:
    print "图片对比失败"

这种方式虽然比较常见,但是如果屏幕分辨率一旦改变,就使得对比结果无效,代码可移植性很低,不稳定。

方式二、monkeyrunner文字对比

1.EasyMonkeyDevice

这种文字对比方式,主要是利用EasyMonkeyDevice中的exists(By selector)和getText(By selector)两种方法来判断。
核心代码如下(代码中的packageName和activity要换成自己需要的包名和活动名):

#coding:utf-8
from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice,MonkeyImage
#提供了根据ID进行访问
from com.android.monkeyrunner.easy import EasyMonkeyDevice
#根据ID返回PyObject的方法
from com.android.monkeyrunner.easy import By
#连接设备
device = MonkeyRunner.waitForConnection()
#packageName、activity根据自己的需求获取,启动app
componentName = packageName + '/' + activity
device.startActivity(component = componentName)
#必须在activity启动之后
easy_device = EasyMonkeyDevice(device)   
#判断结果页面中是否有特定控件ID:如果有,则获取该控件上的文本;若没有,则直接出对比结果 
if easy_device.exists(By.id('id/btn_loginout')) == True:
    text = easy_device.getText(By.id('id/btn_loginout'))
    if text.encode('utf-8') == '注销':
        print "login success"
    else:
        print "login failed"
else:
    print "login failed"

注:控件text需要具有唯一性。

2.HierarchyViewer

这个方法的API和EasyMonkeyDevice类似,主要用到findViewById和getText来判断。

核心代码如下:

device=MonkeyRunner.waitForConnection()
hierarchy_viewer = device.getHierarchyViewer()
login_button = hierarchy_viewer.findViewById('id/btn_loginout')
text =  hierarchy_viewer.getText(login_button)
print text.encode('utf-8')

还可以根据EasyMonkeyDevice的getFocusedWindowId接口来获取当前页面的窗口ID,即activity,或者,根据HierarchyViewer的getFocusedWindowName来获取当前页面的packageName/activityName。从而方便monkeyrunner脚本中各页面的切换。

注:每个窗口的activity并不是唯一的,所以当activity相同时,需要activity的Fragment来判断当前页面。

上一篇下一篇

猜你喜欢

热点阅读