Android自动化测试之Monkeyrunner从零开始(三)

时间:2022-09-07 21:32:32

转自http://www.51testing.com/html/81/22381-854342.html

时光过得太快了,一晃离上一篇monkeyrunner系列的博客已经一年多了。这一年多时间经历了太多改变,一直没时间好好去总结和分享。直到今天在微博上和朋友聊到monkeyrunner,才想起趁这个机会再总结一下之前所积累的一些经验和技巧,在这里再次和大家分享下,也算是将这个从零开始系列再进一步,呵呵。

现在说起Android端的功能自动化测试工具,其实还是蛮多的了,像很早就风靡的Robotium,后起之秀Uiautomator,跨平台的appium等,几乎从工具这个层面还是可以满足大家的要求了。MonkeyRunner作为其中的一员,其实并不能算是特别好用的一款,我平时用得就不多。因为毕竟从SL4A衍生出来,不是基于 Android的直系血亲Instrumentation,天生就有运行速度慢,容易出错,可用的接口少等缺点,可谓是有点“先天不足”。当然优点还是有的,比如基于Jython语言来编写,语法简单易学,可以跨应用,可以直接以ID来操作测试,不需要签名等其他工具不具有的一些特点,所以有时也有它的用武之地,大家学习和掌握它之后应该也有一些机会是可以用到它的,紧急时候救救急啥的也还不错。

既然Monkeyrunner先天不足,如果想要让它好用的话,那当然就只能后天补足了。前段时间在网上找到一个非常强大的 monkeyrunner的第三方库wrapEasyMonkey,是基于EasyMonkeyDevice来二次封装的,并加入了自动异常处理、失败重试、case管理、断言、获取控件上的文本等,大大增强了monkeyrunner本来的功能,使得monkeyrunner强大了不止一个级别。除了运行还是比较慢这个没法解决之外,可以说其他的几乎都有了较大的改善。说句实话,原生monkeyrunner几乎没法直接使用。所以建议选用 monkeyrunner作为自己的测试工具的童鞋,都可以去找找这个包来用用,确实挺好用的。具体的用法我这里就不啰嗦了,作者的教程还是写得蛮细的,相信有一定python基础的童鞋都应该看得懂。但在这里,我想重点分享的还不止这个第三方包,还有我个人对这个包的一些改进的想法。在 wrapEasyMonkey的作者提供的包里,很多方法都是根据控件ID来操作的,比如getView(self,id)。但经常做Android自动化测试的朋友肯定都知道这里有个问题,很多时候我们的app里的控件要么没有ID,要么ID值就是重复的。在这种情况下,根据ID肯定是无法获取到这个控件的引用的。那么怎么办呢?我当时也是为这个问题非常地苦恼,但后来我仔细研究了Hierarchy Viewer得到的控件树形图,其实我们完全可以基于每个控件所在的子节点的位置,结合python函数的不定参数的特性,去获取任意已知ID的父节点的任意子节点的引用,然后再用这个引用作为参数去获取其对应的文本、断言等等,就方便多了,再也不会受到没有ID或者是ID重复的限制了。当然,要达到这样的功能需要修改原来那个包里的部分函数。

这里我举个实际例子来说明一下吧。假如我们现在就来测试一下Android自带的contact 联系人那个app,假设我们先加上了一个联系人,现在我们用monkeyrunner来验证一下我们加入的联系人的信息是否正确。这里实际上就涉及到几个比较重要的操作:1. 获取对应的控件 2. 判断上面的文字是否和预期一致,也就是下面图上三个红色框所表示的部分。hierarchy viewer里的完整的图比较大,这里显示不了,所以大家可以自己通过这个hierarchy viewer来看看。

Android自动化测试之Monkeyrunner从零开始(三)

通过hierarchy viewer里显示的树形结构来看,这里显示电话号码158xxxxxxxx的控件和邮箱地址41420872@qq.com的控件ID都为text2,很明显,重复了。所以没法用原包里的getView(self,id)方法来获取这个view的引用。那是不是就没办法了呢?其实我们可以换个角度来看,即不从ID角度,而是从这个控件树的节点角度来思考如何获得控件的引用。我们可以看到在hierarchy viewer图中的每个控件所对应的框形中,右下角都有一个数字,如下图中红色框中的0和1所示。

Android自动化测试之Monkeyrunner从零开始(三)

其实这个数字就是该控件在同级兄弟节点中的索引值,我们知道这个索引值后,就可以根据parentView.children[index]属性来获取任意父节点所对应的子节点的对象引用。其中的parentView可以是树形图中有有效ID的任意父节点,然后利用python函数的可变参数列表特性来传入所需控件的索引列表即可构造出得到任意节点引用的字符串,从而得到其引用。核心代码如下:

def getChildView(self, parentId, *childSeq):
    hierarchyViewer = self.device.getHierarchyViewer()
    str_getchildview="hierarchyViewer.findViewById('" + parentId +"')"    
    for index in childSeq:       
        str_getchildview+=('.children[' + str(index) + ']')         
    exec 'child_view=' + str_getchildview
    return child_view

现在我们来看看这段代码如何使用。比如我们刚刚那个联系人的详细信息界面的联系人号码控件的hierarchy viewer截图如下:

Android自动化测试之Monkeyrunner从零开始(三)

联系人号码控件的对象引用可由这个语句获得:phone_number=device.getChildView('id/contact_data', 0, 0, 1, 0)。其中“contact_data”这个是其父对象的ID,这个ID必须符合两个条件,即有效和唯一。通过这个ID,我们就可以得到它的任意子孙节点的引用,0,0,1,0则是联系人控件在对象树中的索引链上的位置,大家对照着图应该很容易理解。有了这个引用之后,我们就比较好对所有以ID作为参数的方法来做改造了,你想改哪个就改哪个。这里我举个例子,比如原来wrapEasyMonkey包里有个方法是用来取得控件上的文本的 getTextById(self,id),这也是我们要验证文字时所必须要用到的方法。但如上图所示,其实联系人的控件的ID是有重复的(跟邮箱地址那个控件的ID重复),我们不可能用这个重复的ID去调用getTextById方法,所以我们就只能根据刚刚我写的那个方法来先得到联系人控件的引用,再将获取文本这个方法改成根据传view的引用来获取文本。核心代码如下:

def getText(self,view):
    if view != None:        
         return (view.namedProperties.get('mText').toString().split('=')[1]).encode('utf8')

这下就要灵活很多了,就算你要获取那个对象没有ID或者ID是重复的,都不影响你的操作,因为它在对象树中的位置不可能重复,呵呵。

好了,这次就说这么多了吧。这里我只是就我自己的一些想法和感受写一些抛砖引玉的文字,有不对的地方欢迎各位指正,当然也欢迎各位看官提出更多的想法,一起来改进我们的工具,将Android平台的自动化做得更好,呵呵。