因为前段时间业务需求,需要我们测试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 mr
from com.android.monkeyrunner import MonkeyDevice
from com.android.monkeyrunner import MonkeyImage as mi
from com.android.monkeyrunner.easy import EasyMonkeyDevice, By
from com.android.chimpchat.hierarchyviewer import HierarchyViewer
from com.android.hierarchyviewerlib.models import ViewNode
try:
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 password
def 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目录下。然后运行下面的命令,以下是我的运行结果:
这个是页面启动,如果你的程序并没有运行,他可以找到你的安装路径,并启动。当然,安装路径要在程序中自己设置,通常,我们可以通过在命令行去查看。
这是测试中的输出,我们可以在模拟器中看到自动化测试的过程,就是这样啦~