- http://www.2cto.com/kf/201411/356056.html
- 使用Monkeyrunner进行Android自动化的总结
使用Android自动化的方式,不仅可以用来对Android APP进行自动化测试,同样可以用来进行一些其他非常有意思的自动化任务.常用的自动化工具有Monkeyrunner, Robotium, Appium等.Monkeyrunner是Android自带的自动化测试工具,允许用户对Android设备的UI界面进行元素提取,执行touch 和drag等操作,配合HierarchyViewer等模块可以非常方便地进行自动化操作.
首先,用户需要安装好Android开发环境,同时运行Monkeyrunner脚本需要安装Jython环境.Jython允许使用Python 的语法格式来编写自动化测试代码,因此对于Python开发者而言非常有优势.Python中的一些个别模块不能直接用于Jython中,这时就需要安装 适用于Jython版本的,具体方法可参考
http://stackoverflow.com/questions/3256135/importing-python-modules-in-jython. 如安装bottle模块, jython ez_setup.py bottle,然后在使用时导入该模块即可.
1
2
3
|
import sys sys.path.append( '/home/jython2.5.3/Lib/site-packages/bottle-0.12.7-py2.5.egg' ) from bottle import Bottle, run, request, response, get, post |
使用Monkeyrunner进行Android自动化大概可以分为以下几种类型的操作:设备及UI界面操作,UI界面元素提取,截图对比等.
1, 设备及UI界面操作
其实,涉及到Android设备的操作,使用开发环境自带的adb已经足够了,而Monkeyrunner也是将adb操作封装了以下而已.常见adb操作如下:
1
2
3
4
5
6
7
8
|
adb install xxx.apk 安装apk文件 adb shell am start -an com.xxx.xxx/.MainActivity 启动APP adb shell am force-stop com.xxx.xxx 停止该APP adb shell input keyevent KEYCODE_HOME 模拟Android的HOME按键 adb -s emulator- 5554 shell input text test_to_input 针对特定的一个模拟器进行操作 adb shell input tap x y 模拟屏幕touch操作 adb shell input swipe x1 y1 x2 y2 模拟屏幕滑动操作 adb devices 查看所有在线的Android设备. |
详细的adb命令,可以通过adb -h来查询.而Monkeyrunner中对设备的操作如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice device = MonkeyRunner.waitForConnection( 5 , "emulator-5554" ) device.shell( "am start -an com.xxx.xxx/.MainActivity" ) device.touch( 250 , 450 , 'DOWN_AND_UP' ) device.drag(( 1080 / 2 , 1700 ),( 1080 / 2 , 400 ), 0.5 , 1 ) device.type( "text to type" ) device.shell( "input text" + "text to input" ) device.press( "KEYCODE_HOME" ) # 另外,也可以通过id来进行touch操作,此时可以引入By模块,可以非常方便通过id寻找对应的元素. from com.android.monkeyrunner.easy import EasyMonkeyDevice, By easy_device = EasyMonkeyDevice(device) easy_device.touch(By.id( 'id/button1' ), easy_device.DOWN_AND_UP) |
以上方式其实与adb shell的操作是一致的,只是方便用户在Jython脚本文件中调用而已.
2, UI界面元素提取
Monkeyrunner可以通过HierarchyViewer来对UI界面的元素进行解析,解析的结果与DDMS及Android Studio中的Android Device Monitor保持一致.
首先需要先对UI界面进行解析,然后即可通过元素id和其他的属性来提取该元素,并对其所有属性进行解析.
1
2
3
4
5
6
7
8
9
10
|
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice from com.android.chimpchat.hierarchyviewer import HierarchyViewer device = MonkeyRunner.waitForConnection( 5 , "emulator-5554" ) hViewer = device.getHierarchyViewer() # 对当前UI视图进行解析 content = hViewer.findViewById( 'id/content' ) # 通过id查找对应元素 memberView = content.children[ 0 ] text = memberView.namedProperties.get( 'text:mText' ).value.encode( 'utf8' ) desc = memberView.namedProperties.get( 'accessibility:getContentDescription()' ).value.encode( 'utf8' ) mleft = memberView.namedProperties.get( 'layout:mLeft' ).value.encode( 'utf8' ) height = memberView.height |
使用HierarchyViewer来解析界面的层级关系,并根据id来查找特定元素是我们常用的做法.然 而,Android APP中,会有很多元素是没有对应的id的(这一点,可以通过DDMS或者AVD中解析结果看出来),那么此时,我们如果要精准地找到一个特定元素,就只 能通过进一步解析某个元素的children来实现,会比较麻烦,但往往是非常精准的.
需要注意的是,使用HierarchyViewer并通过id来查找元素偶尔会出错,提示找不到对应的元素.如果遇到实在难以解析出来的元素,可以考虑使 用另一个模块AndroidViewClient进行解析.原理也很类似.甚至有时候,写法比HierarchyViewer简洁得多.
1
2
3
4
5
6
7
|
vc = ViewClient(device=device, serialno= "emulator-5554" ) content = vc.findViewById( 'id/content' ) memberView = content.children[ 0 ] text = memberView.getText() x = memberView.getX() y = memberView.getY() height = memberView.getHeight() |
AndroidViewClient 的项目地址是https://github.com/dtmilano/AndroidViewClient.使用时候有个注意事项,我们先将 AndroidViewClient写入环境变量中,然后要先导入AndroidViewClient的模块,之后再导入Monkeyrunner及相应 地其他模块,否则会出现找不到AndroidViewClient的错误.至于为什么,大家可以自己尝试一下就明白了.
1
2
3
4
5
6
7
8
9
10
|
import sys reload(sys) sys.setdefaultencoding( "utf-8" ) ANDROID_VIEW_CLIENT_HOME = os.environ[ 'ANDROID_VIEW_CLIENT_HOME' ] sys.path.append(ANDROID_VIEW_CLIENT_HOME + '/src' ) from com.dtmilano.android.viewclient import ViewClient, View from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice from com.android.monkeyrunner.easy import EasyMonkeyDevice, By from com.android.chimpchat.hierarchyviewer import HierarchyViewer |
不过以上两种方式,都有可能出现UI元素解析失败的情况,原因可能是Android相应工具自身的不完善导致的.因为DDMS和AVD也会经常出现无法解析某个UI界面的情况.
具体的应用场景大家自己斟酌吧,总之,能够完美获取到所需元素即可.
3, 截图对比
这是Monkeyrunner非常有特色的一种方式,常用于通过设备屏幕前后的对比来获取对执行结果的判断.
1
2
3
4
5
6
7
8
9
10
11
|
path = "/tmp/images" image = device.takeSnapshot() # 截图 image.writeToFile(path+ "主页面" .decode( 'utf-8' )+now+ '.png' , 'png' ) #下面就开始对之前的截图进行对比了 #去文件中找到我们要对比的图片,与该截图image1进行对比 result = MonkeyRunner.loadImageFromFile( '/tmp/images/result.png' ) #判断图片相识度是否是为 90 % if (image.sameAs(result, 0.9 )): log.write( "图片对比成功……
" ) else : log.write( "主页面图片对比失败……
" ) |
以上,就是通过Monkeyrunner进行Android自动化的一些基本内容.
下边,将大家容易遇到的一些坑记录下来,造福广大人民群众.
Monkeyrunner容易遇到的一些坑:
1, 中文输入的问题
Monkeyrunner默认只支持Ascii编码,所以遇到中文,目前是不能通过adb的input和type进行输入的.那么可以采用复制到PC剪贴板,然后到Android模拟器里边进行粘贴的方式.
但需要注意的是,Android模拟器里边的剪贴板的内容是当前PC的焦点从PC桌面环境切换到模拟器界面瞬间时的剪贴板内 容.常见情况是,通过Monkeyrunner脚本文件将PC环境中剪贴板内容向Android模拟器粘贴时,往往会出现粘贴不上我们想要的内容.此时, 出于调试目的,我们会检查在当前PC环境的剪贴板中,是否是我们需要的内容.然后将鼠标焦点移入模拟器中,常常发现能够粘贴上所需的正确内容.,然而,这 其实是一个时间差的原因,即PC中的剪贴板内容正确,然后切换到模拟器界面,剪贴板内容是从PC环境带过来的,当然是正确的了.相反,我们在 Monkeyrunner脚本执行后,在剪贴板操作之前即将当前PC的焦点切换到模拟器中,会发现剪贴板内容是不正确的.说得有点乱,大家可以好好琢磨, 自己实践一下.
github上有位同学写了一个小的工具,可以非常方便地执行Android模拟器中的剪贴板操 作,https://github.com/bingwei/inputchineseviaadb.非常好用,大家可以试一试.当然,遇到一些特殊字 符,还是需要做一些简单地转义等操作的.
2, Monkeyrunner脚本中各个操作的耗时问题
在Monkeyrunner脚本执行过程中,使用HierarchyViewer以及AndroidViewClient进 行界面元素解析时,会发现findViewById操作的时间消耗很大.尤其是该UI界面上元素非常多的时候,耗时非常明显.然而,涉及到界面切换时,我 们常常会根据当前界面中是否包含某个id的元素来判断界面是否切换成功.那么,在大多数情况下,我们没有必要根据id来判断当前界面,通过 windowName = device.getHierarchyViewer().focusedWindowName()这种方式,已经足够我们进行绝大多数的UI界面判断 了,并且在效率上绝对不是一个数量级的提升.
3, 涉及到UI界面之间切换的算法问题
我们常常会遇到,明明就在几个特定的UI界面之间相互切换,但由于Android自动化环境及工具自身的不稳定性,经常导致 屏幕切换延迟,点击或切换不成功,APP卡住甚至闪退等一些非常苦恼的问题.那么这就是考验编码能力和算法功底的时候.在这一点上,非常感谢教授同学的帮 助,赞一个.
我们可以预先定义一个所有常见界面的focusdWindowName及屏幕切换所需的操作行为的结构体,如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
SCREEN_SWITCH_ACTION = { 'Activity1' : { 'Activity1' : None, 'Activity2' : ( 'LEFT_DOWN' , 'Activity1' ), # 如,从Activity2切换到Activity1需要做LEFT_DOWN的操作. 'Activity3' : ( 'LEFT_DOWN' , 'Activity1' ), }, 'Activity2' : { 'Activity1' : ( 'MID_DOWN' , 'Activity2' ), 'Activity2' : None, 'Activity3' : ( 'MID_DOWN' , 'Activity2' ), }, 'Activity3' : { 'Activity1' : ( 'RIGHT_DOWN' , 'Activity3' ), 'Activity2' : ( 'RIGHT_DOWN' , 'Activity3' ), 'Activity3' : None, }, } |
如上,该字典中key是目标屏幕,其value值即代表了从当前屏幕切换到目标屏幕所需的操作行为.其中,LEFT_DOWN等可以是简单地touch一 个button,也可以写出一个负责的根据界面及元素来决定操作行为的负责操作的集合.有了如上的这种结构体,我们只需要写一个对应的算法,在屏幕切换时 从该结构体中解析操作并执行即可.诸如屏幕等待,失败重试之类的容错机制,都可以随意添加了.
其实,涉及到这个算法层面的问题,研究和改进的空间实在是太大了.有兴趣的同学可以更深入的讨论,欢迎指教.
当然,除了Monkeyrunner, Robotium和Appium等工具也都是使用率非常高的,各有优劣吧.
以上这些,就是本次Monkeyrunner自动化的一些总结,写的比较简略,欢迎批评指正.