Android自动化学习笔记:编写MonkeyRunner脚本的几种方式
----------------------------------------------------------------------------------------------------------------------------
小记: 之前用的是公司自己研发的自动化工具,对市面开源的自动化工具知之甚少,所以开始自学开源的自动化工具。
初步学习中,难免会有疏漏和想不到的地方,随着不断深入的了解,如有新的体会,会及时修改,不断进步。
2014-10-17:初版
2014-10-20: 更新通过ID写脚本的实例
2014-10-23:更新HierarchyViewer提示和获取ID的方法
2014-10-24:更新第三方插件内容
2014-10-27:更新Q&A--解决了困扰两天的问题
2016-2-24:删除一种方式
------------------------------------------------------------------------------------------------------------------------------
提示:
如果HierarchyViewer无法在市场上的android手机上使用,使用的条件是手机必须是工程机:
1.手机已经Root
2.设备支持启动view service
二者缺一不可。
翻译过来意思就是说:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器
网上有一些如何把手机root和打开view service的文章,建议不要尝试,手机容易变砖或者不断重启
如果实在按耐不住想拿自己手机试试,可以访问这里:如何在Root的手机上开启ViewServer,使得HierachyViewer能够连接
================================================================================
研究monkeyrunner已经有5天了,前面更新了几篇笔记,先总结一下感觉:
- MonkeyRunner可以通过获取坐标点去点击目标对象,在引入EasyMonkeyDevice后可以根据ID进行点击对象
- MonkeyRunner也有第三方的插件,增加了一些实用方法,不过没有花时间尝试
- Eclipse上写Python代码时,很多对象没有成员函数提示(即使导入过jar包也无用,不过如果写过同样的代码,放在同包的其他文件里,会有部分成员函数的提示信息) :如MonkeyRunner.waitForConnection()获得device对象后,后面devie.不能自动提示可用成员函数
- 性能不是很好,在使用hierarchyviewer获取ID时,明显感觉需要很长时间
- 稳定性也不是很好,在使用hierarchyviewer获取ID时,有时候成功,有时候会失败
- 没有任何junit的继承,尝试使用unit框架来使用MonkeyRunner,不过遇到importError。
- 官网API更新不及时,资料很少,网上找来找去,内容基本都相差无几
- 不过相较robotium支持多设备连接,这个确实是一大优点
今天内容是整理一下最新学到的monkeyrunner脚本的几种方式。
1. 通过光标定位,采用上下左右导航键控制(记忆中是Android刚出来的时候,寝室室友买了个HTC的机子有导航键,现在的机子基本都是直板,所以……)
2. 通过指定坐标的方式(这种方式是最直接的,也是最笨的,因为不同设备的分辨率不同,脚本不可复用,不过可以直接计算出新的坐标,以后再说……)
详细测试脚本可参照:Android自动化学习笔记:MonkeyRunner官方介绍和简单实例 :实例是用HierarchyViewer得到坐标,然后touch坐标或者press key event写的monkeyrunner脚本。
触摸:touch (integer x, integer y, integer type) Sends a touch event specified by type to the screen location specified by x and y.
点击:press (string name, dictionary type) Sends the key event specified by type to the key specified by keycode.
3. 通过录制回放的方式(这种方式能够迅速生成一个脚本,不过不是python的脚本)
详细测试脚本可参照:Android自动化学习笔记:MonkeyRunner录制和回放
4. 通过获取控件文本的方式(用于apk里控件文本不相同的时候使用,所以如果文本显示相同的话,不可使用,这需要采用第三方插件viewClient)
详细配置和使用方法可参考: https://github.com/dtmilano/AndroidViewClient 自己去看咯.
已经独立成单独一个工具,可以忽略。
5. 通过获取控件ID的方式(这种方式比较合适,写一次脚本适用于不同分辨率的手机,但是也需要APK的ID不重复)
下面对calculator.apk来写monkeyrunner脚本:
得到ID: 打开HierarchyViewer->点击Load view Hierarchy
A. 第一种写法:获取HierarchyViewer对象
这里我们也有两个方法:
①可以引入 from com.android.chimpchat.hierarchyviewer import HierarchyViewer
②也可以直接通过hierarchyviewer = device.getHierarchyViewer()来创建
monkeydevice里的源码:
示例:
获取控件viewNode->得到中心点坐标->选择
''' Created on Oct 20, 2014 @author: deldong ''' import sys from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice,MonkeyImage logFile = open("C:\\monkeyrunner_log.txt","a+") #连接设备 device = MonkeyRunner.waitForConnection() if not device: print("Please connect a device to start!") logFile.write("Please connect a device to start!\n") else: print("Device Connected successfully!") logFile.write("Device Connected successfully!\n") package = 'com.android.calculator2' activity = 'com.android.calculator2.Calculator' runComponent = package + '/' + activity #启动计算器 print("Start calculator") logFile.write("Start calculator") device.startActivity(component=runComponent) #等待时间 print("Wait 15s") logFile.write("Wait 15s") MonkeyRunner.sleep(15) #获取控件对象 print("Get widget object") logFile.write("Get widget object") hierarchyviewer = device.getHierarchyViewer() #得到8,9,*,=的viewnode对象 #举例:viewNode对象的值:com.android.calculator2.CalculatorDisplay@b3ffd170 viewDigit8 = hierarchyviewer.findViewById("id/digit8") viewDigit9 = hierarchyviewer.findViewById("id/digit9") viewMul = hierarchyviewer.findViewById("id/mul") viewEqual = hierarchyviewer.findViewById("id/equal") #获取控件的中心坐标 #举例:pointView的中心点坐标的值:Point {240, 116} print("Get location of view") logFile.write("Get location of view") pointViewDigit8 = hierarchyviewer.getAbsoluteCenterOfView(viewDigit8) pointViewDigit9 = hierarchyviewer.getAbsoluteCenterOfView(viewDigit9) pointViewMul = hierarchyviewer.getAbsoluteCenterOfView(viewMul) pointViewEqual = hierarchyviewer.getAbsoluteCenterOfView(viewEqual) #计算8*9=72的结果 print("touch 8*9=") logFile.write("touch 8*9=") MonkeyRunner.sleep(5) device.touch(pointViewDigit8,'DOWN_AND_UP') device.touch(pointViewMul,'DOWN_AND_UP') device.touch(pointViewDigit9,'DOWN_AND_UP') device.touch(pointViewEqual,'DOWN_AND_UP') logFile.close()
B. 第二种写法:通过EasyMonkeyDevice类和By类来调用控件ID
''' Created on Oct 23, 2014 @author: deldong ''' import sys import math reload(sys) sys.setdefaultencoding("utf-8") from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice from com.android.monkeyrunner.easy import EasyMonkeyDevice from com.android.monkeyrunner.easy import By #connect device print('connect device!') device = MonkeyRunner.waitForConnection() #start activity print('start activity') package = 'com.android.calculator2' activity = 'com.android.calculator2.Calculator' runComponent = package + '/' + activity device.startActivity(component=runComponent) MonkeyRunner.sleep(15) #init easymonkeydevice object ,this is init method print('init easymonkeydevice') easy_device = EasyMonkeyDevice(device) print('Tap 8') easy_device.touch(By.id('id/digit8'),MonkeyDevice.DOWN_AND_UP) MonkeyRunner.sleep(1.0) print('Tap *') easy_device.touch(By.id('id/mul'),MonkeyDevice.DOWN_AND_UP) MonkeyRunner.sleep(1.0) print('Tap 9') easy_device.touch(By.id('id/digit9'),MonkeyDevice.DOWN_AND_UP) MonkeyRunner.sleep(1.0) print('Tap =') easy_device.touch(By.id('id/equal'),MonkeyDevice.DOWN_AND_UP) MonkeyRunner.sleep(1.0) pic = device.takeSnapshot() pic.writeToFile('./result.png','png') print('test finished!')
C. 第三种写法:通过wrapeasymonkey来写
先上网址(打不开的用代理) :
https://sourceforge.net/projects/wrapeasymonkey/
http://blog.whoistester.com
中文博客:http://lihao.cf/category/my-work/wrapeasydevice/
在官网上复制的内容:
本文章内容在仅windows 7上经过试验,使用该库主要目的,
1. 将monkeyDevice 与 easyMonkeyDevice统一封装到库
2. 进行自动化操作时, 增加对异常处理,防止异常退出 (例如 当某textview未显示出来时, 捕捉对该textview的操作异常,防止测试脚本异常退出. 如果手动在脚本里加入sleep语句, 但sleep的时间难以指定)
3. 增加了一些比较便捷的函数, 降低了使用 monkeyDevice 和 easyMonkeyDevice 的复杂度。
另需要说明的是: 该wrapEasyMonkey 库是加入了androidviewclient库。 androidviewclient库是Diego Torres Milano 开发的。
如何使用呢。 请先到sourceforge进行下载wrapEasyMonkey源码。(可能需要挂代理)
Q&A:
问题1:执行时,出现importError : no module named xxx的异常:
解决方法:
这个问题比较麻烦,因为直接用python + 文件方式,就可以正常执行。
但是用monkeyrunner + 文件的方式,就会有importError产生。
捣鼓了几天,一直没搞清楚到底是哪里的问题。不过今天很开心的是,尝试出一个临时解决这个问题的方式。
写了一个简单的脚本说明:
package1包里有个testPy文件:
import sys print(sys.path) from package2 import helperPy helperPy.printHelper() if __name__ == '__main__': pass
package2包里有个helperPy文件,里面有个printHelper方法
''' Created on Oct 27, 2014 @author: deldong ''' def printHelper(): print("this is print helper") if __name__ == '__main__': pass
执行:
可以看出,环境变量里,只有package1的文件路径,没有package2的。
那怎么办呢,解决方式就是,咱们自己把package2给加进去。
import sys sys.path.append("C:\\Nokia\\adt-bundle-windows-x86-20140702\\eclipse\\workspace\\项目名")
解决问题灵感来自:
1. Monkeyrunner ImportError: No module named util
问题2: wrapEasyMonkey.py脚本报错,提示:'ImportError: cannot import name ViewNode'
解决方法:将wrapEasyMonkey.py脚本中的from com.android.hierarchyviewerlib.device import ViewNode修改为from com.android.hierarchyviewerlib.models import ViewNode