使用python和android模拟器做android程序的自动化测试

来源:互联网 发布:mac俄罗斯红色号 编辑:程序博客网 时间:2024/06/07 02:02

  因为前段时间业务需求,需要我们测试Android软件性能,刚开始我们使用的是Monkey+Monkeyrunner +python脚本。网上也有很多资料来讲解如何利用monkeyrunner来录制脚本,使用最多的都是通过记录坐标的方式来录制,但因为我们在程序操作中,通常都是在同一块显示区域实现页面的相互跳转,所以这种方法并不能够满足我们的需要,我想到了使用id来做,因为id能够保证控件的唯一性,并且也可以精确地定位到控件的位置。我是因为这个项目自学python,所以对python这方面很多知识并不了解,于是去网上找了很多资料,但是没有一篇完整的代码可以用来实现,我也是查了好多资料,才渐渐摸索出了实现方法,特此记录。


一.准备工作

   在做测试之前,要保证我们的测试环境可以正确运行,需要我们先去搭建测试环境。

   (1)保证电脑上已经安装了jdk并且环境变量等的配置已经完成

   (2)安装了eclipse,用于android的开发,相信做android的人应该这些都是会用到的吧

   (3)下载SDK并且完成SDK的配置(因为我们需要使用到SDK文件夹tools中的一些工具工具,所以这个是必须的)

   (4)安装python IDLE(python编辑器,可以很方便的用来编写python文件),并在环境变量里面配置path

    (5)android模拟器(也可以使用android自带的模拟器,但是启动实在是太慢了,太费时间,果断放弃)

            我使用的是天天模拟器,网上可以下载


二.测试流程

    第一步,打开模拟器,我们可以在eclipse的device下面看到当前的模拟器显示信息,如下图:



注:一定要确保模拟器连接成功了,要不然接下来会有问题。


我们在使用android获取id的时候,都是直接findViewByID(R.id.IDName),系统可以帮助我们识别是在哪一个文档中,但是python却不行,就相当于在android中你写个相对路径,就ok了,但是在python中,必须要使用绝对路径(只是打个比方哈),在python中,如果我们的控件是嵌套在几层布局之中的话,就必须通过它的根布局元素ID去一层一层往下找。通常我们写的界面都很复杂,你要一个一个去找ID,不得累死,万一找漏了一个,就会功亏一篑了,所以我们要向获取正确的ID路径,就要借助于SDK给我们提供的一个工具--hierarchyviewer.bat.

它的路径在   SDK安装目录\tools\hierarchyviewer.bat, 这是我的安装路径:F:\Android_Studio\Android-SDK\tools\hierarchyviewer.bat.

双击它并打来,我们会看到下面这个显示界面,可能你会觉得奇怪,但请不要着急,稍微等一会儿,它会去自动搜索你的模拟器。



如果它没有搜索到你的模拟器,就会是这种现实状态:(不知道什么原因,我的模拟器在eclipse中获取不到了):



也就是说下面没有任何关于模拟器的信息显示,此时回过头去eclipse中的device查看,发现模拟器也不见了。可能很多人会在此时去选择重启模拟器,但是完全不用,我们可以通过命令行去重新连接。

重新连接模拟器:


打开 开始-->运行-->输入cmd-->回车

在命令行中输入adb devices,点击回车,发现没有任何设备连接,此时就需要我们去重新连接。

在命令行中输入adb connect 127.0.0.1:6555,点击回车,显示connected  to 127.0.0.1:6555

然后再次输入adb devices去查看我们的设备列表,就能够识别到了。如下图:


在第一条命令中,127.0.0.1是模拟器的IP地址,6555是端口号,我们可以直接在eclipse-devices窗口中看到的哈~如果使用的是别的模拟器,可以自行查看。很多模拟器是可以直接连接的,但是天天模拟器的连接很麻烦,我也是摸索了很久,才找到这个解决方法。


然后就可以看到我们连接成功后,hierarchyview.bat的界面如下显示:


选中我们的项目,会看到Load View Hierarchy 按钮变为可点击状态,点击该按钮,会出现下面这个界面:


左边这个图中的结构图就是我们当前显示的activity的layout中定义的布局结构图啦~我们可以在左图中的空白区域按住鼠标左键左右拖动来移动图片,也可以通过滚动鼠标来放大缩小图片,当你点击具体的节点时,可以在右边和节点上方看到预览图。并且我们可以很方便的去查看ID,在每一个节点中都会有显示。


接下来,假如说我们要实现一个按钮点击事件,就需要去找到这个按钮的ID,那怎么找呢,首先,我们先找到这个按钮,然后从左图最开始的节点,找出所有带有id值得节点,直到按钮ID所在节点为止,例如,我有一个按钮,id为button_dispatcher,它的路径是:

content->ll_main->linearlayout_side_screensaver->sidebar_fragment->ll_dispatcher->button_dispatcher,因为这个图的根节点是从phoneWindow开始的,我们寻找的时候,只需要从我们的自己的MainActivity加载的层开始寻找就可以了,所以我的路径是从linearlayout_side_screensaver开始的(这里为什么不是从phoneWindow层开始,就要涉及到Android的一个层级结构了,有兴趣的自己去查查哈!这里就不细说了)

好了,不说了,我直接贴代码,还有一个层级结构的图,方便大家比对。


(初来乍到,有很多写的不规范的地方,大家主要看思路就好)

代码如下:

from com.android.monkeyrunner import MonkeyRunner as mrfrom com.android.monkeyrunner import MonkeyDevice from com.android.monkeyrunner import MonkeyImage as mifrom com.android.monkeyrunner.easy import EasyMonkeyDevice, Byfrom com.android.chimpchat.hierarchyviewer import HierarchyViewerfrom com.android.hierarchyviewerlib.models import ViewNodetry:    device= mr.waitForConnection()except Exception:    print 'device connect to VM failed!!!'try:    easyMonkey=EasyMonkeyDevice(device)except Exception:    print 'easyMonkey are not available!!!'try:    hierachy_view=device.getHierarchyViewer()except Exception:    print 'hierachy_view are not available!!!'def returnhierarchyview():    return device.getHierarchyViewer()def returneasyMonkey():        return EasyMonkeyDevice(device)def returnfinviewbyid(idvalue):        #hierachy_view=returnhierarchyview()    return  hierachy_view.findViewById(idvalue)def returnById(idvalue):    return By.id(idvalue)def action_touch(widges,widgesname):        easyMonkey=returneasyMonkey()    print widgesname ,'is available?(True or Faulse)',easyMonkey.visible(widges)    if(easyMonkey.visible(widges)):        print 'the ---',widgesname,'----was touched'        easyMonkey.touch(widges,MonkeyDevice.DOWN_AND_UP)        mr.sleep(1)    else:        return 0    def action_type(widges,value):    easyMonkey=returneasyMonkey()        easyMonkey.type(widges,value)    print 'if ',value,' is available?(True or Faulse)  ',easyMonkey.visible(widges)        print 'input a test case , value = ',value    mr.sleep(1)# test delivery man #admname username#password passworddef testlogin(admname,password):    mr.sleep(2)    #easyMonkey=returneasyMonkey()    #hierachy_view=returnhierarchyview()    try:        view_node1 = returnfinviewbyid('id/sidebar_fragment')        view_node2 = returnfinviewbyid('id/ll_dispatcher')        print('touch button delivery man')        action1=returnById("id/button_dispatcher")        action_touch(action1,'button dispatcher')            view_node3 = returnfinviewbyid('id/content_fragment')        view_node4=returnfinviewbyid('id/account_num')        adm=returnById("id/administrator_number")        action_type(adm,admname)        passw = returnById("id/password_edittext")        action_type(passw,password)        action4 = returnById("id/login")        action_touch(action4,'button login')        print "sleep for 3 seconds!!"        mr.sleep(3)    except NameError:        print 'the id can not be found!!!'        #########################################################def getgoods():    easyMonkey=returneasyMonkey()    #hierachy_view=returnhierarchyview()    layoutview01=returnfinviewbyid('id/linearLayout_body_sreenSaver')        view_node3=returnfinviewbyid('id/content_fragment')    layoutview01=returnfinviewbyid('id/linearlayout_cabinet_2_10')    if layoutview01 is None:        layoutview01=returnfinviewbyid('id/linearlayout_cabinet_4_10')        layoutview02=returnfinviewbyid('id/linearlayout_cabinet_opendoor_back2')        button_back=returnById("id/cabinet_opendoor_back_410")        action_touch(button_back,"cabinet_opendoor_back_410")    else:                linear_openback_view=returnfinviewbyid('id/linearlayout_cabinet_opendoor_back')        button_back=returnById("id/cabinet_opendoor_back_210")        action_touch(button_back,"cabinet_opendoor_back_210")            ##########################################################test the function after login page.def parceldelivery(order,phone):    easyMonkey=returneasyMonkey()    #hierachy_view=returnhierarchyview()   # view_node = returnfinviewbyid('id/content_fragment')    dilivePackage=returnById("id/dilivePackage")    action_touch(dilivePackage,"dilivePackage")    orderId=returnById("id/orderId")    textstr = easyMonkey.getText(orderId)    if(textstr == ''):        action_type(orderId,order)     else:        empty1=returnById("id/empty")        action_touch(empty1,"empty")        #easyMonkey.setText(orderId,"")        action_type(orderId,order)    phoneNum=returnById("id/phoneNum")    text=easyMonkey.getText(phoneNum)    if(text == ''):        action_type(phoneNum,phone)            else:        #easyMonkey.setText(phoneNum,"")        empty2=returnById("id/empty")        action_touch(empty2,"empty")                action_type(phoneNum,phone)            button_next=returnById("id/button_next")    action_touch(button_next,"button_next")    print "sleep for 6 seconds!!"    mr.sleep(6)    getgoods()   # button_diliv_back = returnById("id/button_diliv_back")   # action_touch(button_diliv_back,"button_diliv_back")   def takegoods(identifycode):    sider_layou=returnfinviewbyid('linearLayout_side_sreenSaver')    tackgood_layout=returnfinviewbyid('id/sidebar_fragment')    ll_getpackage=returnfinviewbyid('ll_getpackage')    action1=returnById("id/button_getpackage")    action_touch(action1,'button getpachage')    layoutview01=returnfinviewbyid('id/linearLayout_body_sreenSaver')        view_node3 = returnfinviewbyid('id/content_fragment')    relativeview=returnfinviewbyid('charge_card_number1')    edit_getpackage=returnById("id/take_goods_number")    action_type(edit_getpackage,identifycode)    take_goods_enter=returnById('id/take_goods_enter')    action_touch(take_goods_enter,"take_goods_enter")    print "sleep for 6 seconds!!"    mr.sleep(6)    getgoods()                    ##########################################################      if __name__ == '__main__' :    import codecs    import time    time_format='%Y-%m-%d %X'    try:        codecs.register(lambda name:codecs.lookup('utf-8') if name == 'cp65001' else None)        print('APP start to test...')         print('start robot main activity')        componentName='包名/.MainActivity'        device.startActivity(component=componentName)        print('MainActivity begin to run')        mr.sleep(3)    except Exception:        print 'this is somehing wrong!!!'        #test delivery activity    #test case    #testlogin('123456','000000')    #testlogin('12345678','000000')    #testlogin('123','000000')       #test parcel delivery        #test case    #parceldelivery('1234564','15678909878')    #parceldelivery('123456','156789098')    #parceldelivery('1234','15678909878')    #parceldelivery('','')    #parceldelivery('','15678909878')    #parceldelivery('123456','')    try:        for i in range(1,10):            print '<<<<<<<<<<<<<<<<<<<<<<<<<<','This is the   ',i, '  times to run>>>>>>>>>>'            print 'the current time is  ',time.strftime(time_format,time.localtime())            testlogin('300001','987654')            parceldelivery('1098000000001416801','15983629282')                takegoods('123456')            print '\nthe end time is ',time.strftime(time_format,time.localtime())            print '<<<<<<<<<<<<<<<<<<<<<<<<<<','the End of  ',i, '  times>>>>>>>>>>>\n\n'    except Exception:        print 'this is a exception occur...'        


下面是我的页面的一个层级结构:(因为图片太大了,可能有点看不清):



大家可以比对这个图,就大概能理清思路了。

注意:可能有人会注意到我在程序中使用了sleep来休眠几秒,是因为有时候我们会有弹出框来提示信息,如果在弹出框还没有消失的时候,我们监测到的当前界面就是弹出框界面,而不是原来界面,同样会提示找不到id之类的信息,所以一定要休眠,等到弹出框消失之后,再继续运行,就不会报错啦~

三.运行查看结果

首先,打开命令行:开始->运行->cmd

在命令行中将路径切换到你的android SDK的tools目录所在路径,我是为了方便运行,避免输入路径,直接把python文件放在了tools目录下。然后运行下面的命令,以下是我的运行结果:



这个是页面启动,如果你的程序并没有运行,他可以找到你的安装路径,并启动。当然,安装路径要在程序中自己设置,通常,我们可以通过在命令行去查看。




这是测试中的输出,我们可以在模拟器中看到自动化测试的过程,就是这样啦~

1 0
原创粉丝点击