老司机带你玩转APP的Monkey测试
1,解压ADB压缩包,在cmd窗口下,启动adb服务:
image.pngimage.png
2,使用USB将手机和电脑连接,并将手机设置为开发者模式,使用adb devices命令查看当前连接设备:
image.png3,执行monkey命令
monkey是只皮猴子,是安卓中的一个UI压力测试工具,重在测试稳定性。但是在运用monkey测试app,深受其乱点的烦恼,虽然在指定app的情况下,依然会随机点到app外面,然后再也回不来了,大大降低了其作用。
查阅了网上不少文章,在看这篇文章的时候,突然有了点启发https://blog.csdn.net/liyu520131414/article/details/6935777
这篇文章的思路就是在monkey在执行时候,另外开启一个程序对其进行轮询,如果不在指定的页面,就随机将它拉到app里指定的页面。
当然java我不太懂,只是大致看了下,在执行的时候也没有达到理想的效果,也许是我执行的方式不对,因此参照他的思路,用python写了个轮询脚本,当然功能上也比他写的缩减了不少,暂时只是对连接在电脑上一台手机进行此操作,详细优化步骤如下。
1.monkey脚本
先来看下原先我们执行的脚本
adb shell monkey -p cn.citytag.mapgo -s 2333 --pct-touch 70 --pct-motion 30 --ignore-crashes --ignore-timeouts --monitor-native-crashes --throttle 200 -v -v 500000 >E:\logs\monkey0903.txt
monkey命令的含义就不细说了,网上有超多文章进行了详细解释,绿色--pct-touch 70 --pct-motion 30这两个命令是后面增加的,一个代表点击,一个代表滑动,两个加起来是100,代表不会进行其他事件操作,如轨迹事件,导航事件等等,指定这两种操作后,就极大的降低了monkey点到被测app外面的概率
另外还是会有在执行向下滑动的命令时,打开了通知栏,然后又开始了瞎点的操作。这时可在执行monkey命令之前,先执行禁用通知栏的命令
adb shell settings put global policy_control immersive.full=*
解禁命令:adb shell settings put global policy_control null
这里对monkey的参数做一个注解:
命令参数
可以使用命令 adb shell monkey -help 查看命令参数
1、参数: -p
用于约束限制,用此参数指定一个或多个应用。指定应用之后,monkey将只允许系统启动指定的app;如果不指定应用,将允许系统启动设备中的所有应用。
指定一个应用: adb shell monkey -p com.ifeng.news2 100
指定多个应用:adb shell monkey -p com.ifext.news –p com.ifeng.news2 100
不指定应用:adb shell monkey 100
2、参数:-c
用于约束限制,用此参数指定了一个或几个类别,Monkey将只允许系统启动被这些类别中的某个类别列出的Activity。如果不指定任何类别,Monkey将选择下列类别中列出的Activity:Intent.CATEGORY.LAUNCHER 或 Intent.CATEGORY.MONKEY。要指定多个类别,需要使用多个-c选项,每个-c选项只能用于一个类别。
3、参数:-v
用于指定反馈信息级别(信息级别就是日志的详细程度),总共分3个级别:
默认级别 Level 0:-v
adb shell monkey -p com.ifeng.news2 –v 100:说明仅提供启动提示、测试完成和最终结果等少量信息
日志级别 Level 1:-v -v
adb shell monkey -p com.ifeng.news2 –v -v 100:说明提供较为详细的日志,包括每个发送到Activity的事件信息
日志级别 Level 2:-v -v -v
adb shell monkey -p com.ifeng.news2 –v -v –v 100:说明最详细的日志,包括了测试中选中/未选中的Activity信息
4、参数: -s
伪随机数生成器的seed值。如果用相同的seed值再次运行Monkey,它将生成相同的事件序列
Monkey 测试1:adb shell monkey -p com.ifeng.news2 -s 10 100
Monkey 测试2:adb shell monkey -p com.ifeng.news2 –s 10 100
两次测试的效果是相同的,因为模拟的用户操作序列(每次操作按照一定的先后顺序所组成的一系列操作,即一个序列)是一样的。
5、参数: --throttle<毫秒>
用于指定用户操作(即事件)间的延时,单位是毫秒
adb shell monkey -p com.ifeng.news2 --throttle 5000 100
6、参数: --ignore-crashes
用于指定当应用程序崩溃时(Force& Close错误),Monkey是否停止运行。如果使用此参数,即使应用程序崩溃,Monkey依然会发送事件,直到事件计数完成。
adb shellmonkey -p com.ifeng.news2 --ignore-crashes 1000
测试过程中即使程序崩溃,Monkey依然会继续发送事件直到事件数目达到1000为止
adb shellmonkey -p com.ifeng.news2 1000
测试过程中,如果acg程序崩溃,Monkey将会停止运行
7、参数: --ignore-timeouts
用于指定当应用程序发生ANR(Application No Responding)错误时,Monkey是否停止运行。如果使用此参数,即使应用程序发生ANR错误,Monkey依然会发送事件,直到事件计数完成。
adb shellmonkey -p com.ifeng.news2--ignore-timeouts 1000
8、参数: --ignore-security-exceptions
用于指定当应用程序发生许可错误时(如证书许可,网络许可等),Monkey是否停止运行。如果使用此参数,即使应用程序发生许可错误,Monkey依然会发送事件,直到事件计数完成。
adb shellmonkey -p com.ifeng.news2 --ignore-security-exception 1000
9、参数: --kill-process-after-error
用于指定当应用程序发生错误时,是否停止其运行。如果指定此参数,当应用程序发生错误时,应用程序停止运行并保持在当前状态(注意:应用程序仅是静止在发生错误时的状态,系统并不会结束该应用程序的进程)。
adb shellmonkey -p cn.emoney.acg --kill-process-after-error 1000
10、参数: --monitor-native-crashes
用于指定是否监视并报告应用程序发生崩溃的本地代码。
adb shellmonkey -p cn.emoney.acg --monitor-native-crashes 1000
11、参数: --pct-{+事件类别}{+事件类别百分比}
用于指定每种类别事件的数目百分比(在Monkey事件序列中,该类事件数目占总事件数目的百分比)
--pct-touch{+百分比}:
调整触摸事件的百分比(触摸事件是一个down-up事件,它发生在屏幕上的某单一位置)
adb shell monkey -p com.ifeng.news2 --pct-touch 10 1000
--pct-motion {+百分比}:
调整动作事件的百分比(动作事件由屏幕上某处的一个down事件、一系列的伪随件机事和一个up事件组成)
adb shell monkey -p com.ifeng.news2 --pct-motion 20 1000
--pct-trackball {+百分比}:
调整轨迹事件的百分比(轨迹事件由一个或几个随机的移动组成,有时还伴随有点击)
adb shell monkey -p com.ifeng.news2 --pct-trackball 30 1000
--pct-nav {+百分比}:
调整“基本”导航事件的百分比(导航事件由来自方向输入设备的up/down/left/right组成)
adb shell monkey -p com.ifeng.news2 --pct-nav 40 1000
--pct-majornav {+百分比}:
调整“主要”导航事件的百分比(这些导航事件通常引发图形界面中的动作,如:5-way键盘的中间按键、回退按键、菜单按键)
adb shell monkey -p com.ifeng.news2 --pct-majornav 50 1000
操作事件简介
Monkey所执行的随机事件流中包含11大事件,分别是触摸事件、手势事件、二指缩放事件、轨迹事件、屏幕旋转事件、基本导航事件、主要导航事件、系统按键事件、启动Activity事件、键盘事件、其他类型事件。Monkey通过这11大事件来模拟用户的常规操作,对手机App进行稳定性测试。下面让我们来详细了解这11大事件。
1.触摸事件
触摸事件是指在屏幕某处按下并抬起的操作,可通过--pct-touch参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到。 该事件由一组Touch(ACTION_DOWN)和Touch(ACTION_UP)事件组成,在手机上看到实际操作类似于点击。
2.手势事件
手势事件是指在屏幕某处的按下、随机移动、抬起的操作,即直线滑动操作。可通过--pct-motion参数来配置其事件百分比。
该事件是由一个ACTION_DOWN事件、一系列ACTION_MOVE事件和一个ACTION_UP事件组成的,在手机上看到的实际操作是一个没有拐弯的直线滑动操作。
3.二指缩放事件
二指缩放事件是指在屏幕上的两处同时按下,并同时移动,最后同时抬起的操作,即智能机上的放大缩小手势操作。可通过--pct-pinchzoom参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
该事件起始是一个ACTION_DOWN事件和一个ACTION_POINTER_DOWN事件,即模拟两个手指同时点下;中间是一系列的ACTION_MOVE事件,即两个手指同时在屏幕上直线滑动;结束是由一个ACTION_POINTER_UP事件和一个ACTION_UP事件组成的,即两个手指同时放开。
4.轨迹事件
轨迹事件是由一个或多个随机的移动组成的,有时会伴随着点击。很早之前的Android手机带有轨迹球,这个事件就是模拟的轨迹球的操作。现在的手机几乎都没有轨迹球,但轨迹球事件中包含曲线滑动操作,如果被测程序需要曲线滑动时可以选用此参数。可通过--pct-trackball参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
该事件是由一系列的Trackball(ACTION_MOVE)事件组成的,观察手机上的操作,即为一系列的曲线滑动操作。
5.屏幕旋转事件
屏幕旋转事件是一个隐藏事件,在Android官方文档中并没有记录这个事件。它其实是模拟的Android手机的横屏和竖屏切换。可通过--pct-rotation参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到: [代码] 该事件由一个rotation事件组成,其中degree表示的是旋转方向,顺时针旋转,0表示旋转90度的方向,1表示旋转180度的方向,2表示旋转270度的方向,3表示旋转360度的方向。在执行过程中,可以看到手机屏幕在横竖屏之间不断地切换。
6.基本导航事件
基本导航事件是指点击方向输入设备的上、下、左、右按键的操作,现在手机上很少有上、下、左、右按键,这种事件一般用得比较少。可通过--pct-nav参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
该事件是由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的,点击的就是上、下、左、右四个方向按键。
7.主要导航事件
主要导航事件是指点击“主要导航”按键的操作,这些按键通常会导致UI界面中的动作,如键盘的中间键、回退按键、菜单按键。可通过--pct-majornav参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到: [代码] 该事件是由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的,点击的按键就是中间键和菜单键。
8.系统按键事件
系统按键事件是指点击系统保留使用的按键的操作,如点击Home键、返回键、音量调节键等。可通过--pct-syskeys参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到: [代码] 该事件是由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的,点击的就是上面说到的几个系统按键。
9.启动Activity事件
启动Activity事件是指在手机上启动一个Activity的操作。在随机的时间间隔中,Monkey将执行一个startActivity()方法,作为最大限度上覆盖被测包中全部Activity的一种方法。可通过--pct-appswitch参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到: [代码] 该事件是由一个Switch操作组成的,从手机上看,上面的操作实际是打开了com.android.settings这个应用的一个com.android.settings.Settings的Activity界面。
10.键盘事件
键盘事件主要是一些与键盘相关的操作。比如点击输入框、键盘弹起、点击输入框以外区域、键盘收回等。可通过--pct-flip参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到: [代码] 如日志所示,这里主要是键盘的打开和关闭操作。
11.其他类型事件
其他类型事件包括了除前面提到的10种事件外其他所有的事件,如按键、其他不常用的设备上的按钮等。可通过--pct-anyevent参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到: [代码] 该事件是由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的,点击的按键就是其他的一些系统按键,如字母按键、数字按键等。因为现在手机很少带字母按键或数字按键,所以这个事件一般使用得比较少。
日志输出
输出日志的方法:C:\Documents and Settings\Administrator>adb shell monkey -p 包名 -v 300 >D:\log.txt
2.python脚本
在完善了monkey脚本之后,还是会出现monkey点出去之后回不来的情况,那就写脚本吧。
首先再一次明确目标,就是在monkey乱点点出了被测app后将其拉回来。
直接先把代码丢上来。之前参照的文章里面利用xml作为配置文件,感觉挺简单的,也就直接用了。
配置文件:config.xml
<?xml version="1.0" encoding="utf-8"?>
<config>
<!-- 包名-->
<packagename>com.alashow.live</packagename>
<!-- 主activity-->
<mainactivity>.MainActivity</mainactivity>
<!-- 白名单activity-->
<whiteactivity>
/.MainActivity,
/cn.citytag.live.view.activity.LivePlayerActivity,
/user.maopao.com.user.view.activity.mine.OthersHomePageActivity,
/cn.citytag.live.view.activity.LiveFinishActivity,
/cn.citytag.live.view.activity.LiveSearchActivity,
/cn.citytag.live.view.activity.LiveRankingActivity,
/cn.citytag.live.view.activity.LiveSubHomeActivity,
/cn.citytag.live.view.activity.family.FamilyDetailActivity,
/cn.citytag.live.view.activity.family.FamilyInformActivity,
/cn.citytag.live.view.activity.family.FamilySettingActivity,
/cn.citytag.live.view.activity.family.FamilyNoticeActivity,
/cn.citytag.live.view.activity.tribe.TribeHomeActivity,
/cn.citytag.live.view.activity.tribe.TribeWelfareActivity,
/cn.citytag.live.view.activity.tribe.TribeShopActivity,
/cn.citytag.live.view.activity.tribe.TribePackageActivity,
/user.maopao.com.user.view.activity.bill.MyIncomeTotalDetailActivity,
/cn.citytag.live.view.activity.LivePrepareActivity,
/cn.citytag.live.view.activity.LiveSongEditActivity,
/cn.citytag.live.view.activity.LiveNoticeActivity,
/cn.citytag.base.widget.pictureselector.lib.PictureSelectorActivity
</whiteactivity>
<!-- 检查时间秒-->
<interval>30</interval>
<!-- 检查次数-->
<count>10</count>
</config>
说明:没什么好说明的,都注释出来了,就是字面意思。
python脚本:
import os
import re
import xml.dom.minidom as xmldom
import time
class Mtest():
def __init__(self):
dom = xmldom.parse('config.xml')
root = dom.documentElement
self.packagename = root.getElementsByTagName('packagename')[0].firstChild.data
self.mainactivity = root.getElementsByTagName('mainactivity')[0].firstChild.data
self.interval = int(root.getElementsByTagName('interval')[0].firstChild.data)
self.count = int(root.getElementsByTagName('count')[0].firstChild.data)
self.whiteactivity = root.getElementsByTagName('whiteactivity')[0].firstChild.data.replace('\n','').replace(' ','').split(',')
def get_now_activity(self):
os.system("adb devices")
content = os.popen('adb shell dumpsys activity |findstr "mResumedActivity" ').read() #读取当前页面
pattern = re.compile(r'/[a-zA-Z0-9\.]+')
alist = pattern.findall(content)
macitivity = self.packagename + '/' + self.mainactivity
excuteshell = 'adb shell am start -n'+ macitivity
if alist[0] not in self.whiteactivity:
print('当前activity:'+alist[0])
print('--------------开始返回主activity----------------')
os.system(excuteshell) #可拉回主页面
else:
print('当前activity:'+alist[0]+'不需要返回')
def check(self):
for _ in range(self.count):
time.sleep(self.interval)
self.get_now_activity()
if __name__ == '__main__':
test = Mtest()
test.check()
python脚本这里就要稍微说明下了:
1.这里造了一个类,类的初始化里将config.xml里的内容读取过来运用了xml的一个库
2.get_now_activity函数的作用是获取当前所在activity,判断其是否在白名单里,如果在就做操作,如果不在就拉回主页面(当然你可以设置拉回任何页面,甚至随机)
os.system("adb devices") 这句不写也没关系,其实是获取当前连接的手机设备信息
image.png
adb shell dumpsys activity |findstr "mResumedActivity" 这条adb命令就是获取当前所在activity。如果你查看app的activity也很简单,你手机连着执行这条命令就可以了,这条命令执行后大致是这样的结果:
image.png
是不是数据不够单纯,与config.xml里的白名单数据相比是不是多了很多东西。
pattern = re.compile(r'/[a-zA-Z0-9.]+')
alist = pattern.findall(content)
这时候可以用正则表达式将其取出输出alist[0],大致是这样的:
image.png
页面取出来之后就可以对其进行对比了,就是那个if判断
接下来就是在判断到activity不在白名单里面之后,启动指定activity 利用这个命令'adb shell am start -n'
3.最后一个函数就是利用config.xml里的配置,设置多长时间去轮询一次,然后一共轮询多少次。如30s轮询一次,那么一个小时就能轮询120次,假设你的monkey执行10个小时,那么你的轮询次数就要设置1200次。
说了这么多,如何执行呢?
1.先执行adb monkey的命令,将monkey跑起来
2.执行python脚本
3.仔细观察是不是就算monkey乱点也能拉回去了呢,<( ̄▽ ̄)/