monkey
monkey 概况
在Android的官方自动化测试领域有一只非常著名的“猴子”叫Monkey,这只“猴子”一旦启动,就会让被测的Android应用程序像猴子一样活蹦乱跳,到处乱跑。人们常用这只“猴子”来对被测程序进行压力测试,检查和评估被测程序的稳定性。
Android官方对这只“猴子”的描述是这样的:Monkey是Google提供的一个命令行工具,可运行在模拟器或实际设备中。它向系统发送伪随机的用户事件,模拟用户的按键输入、触摸屏输入、手势输入等,从而对正在运行的应用程序进行压力测试,目的是看设备多长时间会出现异常,并观察系统的稳定性和容错性能。
Monkey程序是Android系统自带的,其启动脚本是位于Android系统的/system/bin目录的Monkey文件,其jar包是位于Android系统的/system/framework目录的Monkey. jar文件。用户主要是通过adb 命令来启动Monkey的,Monkey在运行时,会根据命令行参数的配置,生成伪随机的事件流,并在Android设备上执行对应的测试事件。同时,Monkey还会对测试系统进行监测,当出现以下三种情况时会进行特殊处理:
❑ 如限定了Monkey运行在特定包上,当监测到试图转到其他包的操作,将对其进行阻止。
❑ 如应用程序崩溃或接收到任何失控异常,Monkey将记录对应的错误日志,并根据命令行参数判断是停止运行还是继续运行。
❑ 如果应用程序发生了程序无响应(application not responding)的错误,Monkey将记录对应的错误日志,并根据命令行参数判断是停止运行还是继续运行。
按照选定的不同级别的反馈信息,在Monkey中还可以看到其执行过程报告和生成的事件。
monkey参数
Monkey启动的命令行脚本为:
monkey [option] <count>
其中,options表示Monkey执行的可配置参数,是可选项(如果不指定options, Monkey将以无反馈模式启动,并把事件任意发送到安装在目标环境中的全部包); count表示Monkey执行的事件数,为必选项。
Options可简单划分为五类:
❑ 基本配置类参数。
❑ 事件类型和频率参数。
❑ 约束限制类参数。
❑ 调试类参数。
❑ 官方隐藏类参数。
以下是针对以上五种类型参数的详细介绍。
1.基本配置类参数
Monkey的基本配置类参数包括帮助参数和日志信息参数。帮助参数用于输出Monkey命令使用指导;日志信息参数将日志分为三个级别,级别越高,日志的信息越详细。
参数 | 说明 |
---|---|
- -help | 输出Monkey 的命令行使用方法 |
-v | 表示反馈信息的级别,Monkey命令中增加一个-v参数,Monkey日志反馈信息的级别会对应增加一个Level。Level 0(缺省值)除启动提示测试完成和最终结果之外,提供较少信息。Level 1(-v-v) 提供较为详细的测试信息,如逐个发送到Activity 的事件。Level2 (-v-v-v) 提供更加详细的设置信息。如测试中被选中或未被选中的Activity 等。举例 adb shell monkey -v-v 10 |
2.事件类型和频率参数
Monkey的事件类参数的作用是对随机事件进行调控,从而使其遵照设定运行,如设置各种事件的百分比、设置事件生成所使用的种子值等。频率参数主要限制事件执行的时间间隔。
参数 | 说明 |
---|---|
-s < seed> | 伪随机数生成器的种子值,如果用相同的种子值再次运行Monkey,他将生成相同的事件序列 举例:adb shell monkey -s 1111 -v 10 |
- - throttle<毫秒数> | 在事件之间插入固定延迟,通过这个选项可以减缓Monkey 的执行速度,如果不指定该选项,Monkey将不会被延迟,时间将尽可能快的生成 |
- -pct-touch<百分比 > | 调整触摸时间的百分比(触摸事件是一个down-up事件,它发生在屏幕闪的某单一位置) |
- -pct - montion <百分比 > | 调整动作事件的百分比(动作事件由屏幕上某处的一个down 事件一系列的伪随机事件和一个up事件组成 |
- -pct -pinchzoom <百分比 > | 调整二指缩放的百分比 |
- - pct -rotation <百分比 > | 调整屏幕旋转事件的百分比(竖屏或横屏) |
- - pct -trackball <百分比 > | 调整轨迹事件的百分比 |
- - pct -nav <百分比 > | 调整“基本”导航事件的百分比(导航事件由来自方向输入设备的up,down ,left,right组成 |
- - pct -majornav <百分比 > | 调整“主要”导航事件的百分比(这些导航事件通常引发图形界面中的动作,如5-way 键盘的中间按键,回退按键,菜单按键组成 |
- - pct -syskeys<百分比 > | 调整”系统“ 按键事件的百分比(这些按键通常被保留,由系统使用,入Home,Back,StartCall ,EndCall 及音量控制键 |
- - pct -appswitch<百分比 > | 调整启动Activity 的百分比(在随机间隔里,Monkey通过调用startActivity 方法最大限度地开启该package下全部Activity的一种方法 |
- - pct -flip <百分比 > | 调整键盘事件的百分比(键盘事件如点击输入框,键盘吊起,点击输入框以外的区域,键盘收起等) |
- - pct -anyeven <百分比 >t | 调整其他事件的百分比(包罗了所有其他类型的事件,如按键,其他不常用的设备按钮等) |
3.约束限制类参数
Monkey的约束限制类参数的作用是将随机事件运动感的范围限制在一个或多个包或类中
参数 | 说明 |
---|---|
-p <包名 > | 如果此参数指定了一个或几个包,Monkey将只允许系统启动这些包里的Activity ,如果应用程序还需要访问其他包里的Activity( 如选择一个联系人),那些包也需要在此同时指定。如果不指定任何包,Monkey 将允许系统启动全部包里的activity ,要指定多个包,需要使用多个- p 选项,每个- p 选项只能用于一个包 |
-c<类别名 > | 如果用此参数指定了一个或几个类别(Category),Monkey 将只允许系统启动被这些类别中的某个类别列出的Activity 。如果不指定任何类别,Monkey 将选择下类别中列出的Activity :Intent.CATEGORY_LAUNCHER 或 Intent.CATEGORY_MONKEY. 需要指定多个类别,需要使用多个-c 选项,每个-c 选项只能用于一个类别 |
4.调试类参数
通过调试类命令,可以对Monkey进行一些简单的调试,可以快速定位Monkey执行过程中的一些问题。如果用户想监控应用程序所调用的包之间的转换。则可以用 - - dbg-no-events 参数;如果用户想监控内存泄漏,可以用- - hprof参数
参数 | 说明 |
---|---|
- -dbg-no-events | 设置此选项,Monkey将执行初始化启动,进入一个测试Activity 不会再进一步生成事件。为了得到最佳效果,把它与- v,一个或几个包约束,以及一个保持Monkey 运行30秒或更长事件的非零值联合起来,从而提供一个可以监视应用程序所调用的包之间的转换的环境 |
- -hprof | 设置此选项,将在Monkey 事件执行之前和执行之后生成内存快照存放于手机的data/misc 目录。通过对比执行前后的内存快照文件,可以协助定位内存泄漏问题,由于内存快照文件较大,所以要小心使用 |
- - igonre-crashes | 通常当应用程序失控或者发生任何失控异常时,Monkey将停止运行,如果设置此选项,Monkey将继续向系统发送事件,直至计数完成 |
- - igonre-timeouts | 通常当应用程序发生任何超时错误时,Monkey将停止运行,如果设置此选项,Monkey将继续向系统发送事件,直至计数完成 |
- - igonre-security-exceptions | 通常,当应用程序发生许可错误Monkey将继续向系统发送事件,直至计数完成 |
- -kill-process-after-error | 通常,当Monkey由于一个错误而停止时,出错的应用程序将处于运行状态,当设置了此选项,将会通知系统停止发生错误的进程, |
- - monitor-native-crashes | 监视并报告系统中本地代码的奔溃事件 |
- -wait-dbg | 停止执行中的Monkey,直到有调试器和它相连接 |
5.官方隐藏类参数
在Android 官网上还有三个参数是看不到说明的,即为隐藏参数
参数 | 说明 |
---|---|
- -pkg-blacklist-file <黑名单文件 > | 限制Monkey不测试于指定黑名单文档中记录的包,若没有这个参数Monkey会测试系统内所有的包 |
- -pkg-whitelist-file <白名单文件 > | 只测试于指定黑名单文档中记录的包 |
-f <脚本文件 > | 指定Monkey执行用户自定义的脚本文件 |
Monkey事件
Monkey所执行的随机事件流中包含11大事件,分别是触摸事件、手势事件、二指缩放事件、轨迹事件、屏幕旋转事件、基本导航事件、主要导航事件、系统按键事件、启动Activity事件、键盘事件、其他类型事件。Monkey通过这11大事件来模拟用户的常规操作,对手机App进行稳定性测试。
1.触摸事件
触摸事件是指在屏幕某处按下并抬起的操作,可通过–pct-touch参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Touch(ACTION_DOWN):0,(444.0,1716.0)
:Sending Touch(ACTION_UP):0,(447.18365,1728,0087)
该事件由一组Touch(ACTION_DOWN)和Touch(ACTION_UP)事件组成,在手机上看到实际操作类似于点击。
2.手势事件
手势事件是指在屏幕某处的按下、随机移动、抬起的操作,即直线滑动操作。可通过–pct-motion参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Touch(ACTION_DOWN):0:(282.0,750.0)
:Sending Touch(ACTION_MOVE):0:(281.0507,745.5253)
:Sending Touch(ACTION_MOVE):0:(274.9443,743.3276)
:Sending Touch(ACTION_MOVE):0:(269.18774,738.50525)
:Sending Touch(ACTION_MOVE):0:(260.14917,733.6212)
:Sending Touch(ACTION_UP):0:(254.1414,730.6132)
该事件是由一个ACTION_DOWN事件、一系列ACTION_MOVE事件和一个ACTION_UP事件组成的,在手机上看到的实际操作是一个没有拐弯的直线滑动操作。
3.二指缩放事件
二指缩放事件是指在屏幕上的两处同时按下,并同时移动,最后同时抬起的操作,即智能机上的放大缩小手势操作。可通过–pct-pinchzoom参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Touch (ACTION_DOWN): 0:(274.0,193.0)
:Sending Touch (ACTION_POINTER_DOWN 1): 0:(272.80875,198.17978) 1:(26.0,312.0)
:Sending Touch (ACTION_MOVE): 0:(251.31396,198.5104) 1:(24.973522,308.64676)
:Sending Touch (ACTION_MOVE): 0:(240.28494,202.44012) 1:(23.442032,307.8576)
:Sending Touch (ACTION_MOVE): 0:(221.90855,206.75597) 1:(22.903313,306.57507)
:Sending Touch (ACTION_MOVE): 0:(210.28592,212.24286) 1:(17.78174,303.11304)
:Sending Touch (ACTION_POINTER_UP 1): 0:(171.06334,236.1724) 1:(10.3147135,293.79877)
:Sending Touch (ACTION_UP): 0:(161.06638,240.22447)
该事件起始是一个ACTION_DOWN事件和一个ACTION_POINTER_DOWN事件,即模拟两个手指同时点下;中间是一系列的ACTION_MOVE事件,即两个手指同时在屏幕上直线滑动;结束是由一个ACTION_POINTER_UP事件和一个ACTION_UP事件组成的,即两个手指同时放开。
4.轨迹事件
轨迹事件是由一个或多个随机的移动组成的,有时会伴随着点击。很早之前的Android手机带有轨迹球,这个事件就是模拟的轨迹球的操作。现在的手机几乎都没有轨迹球,但轨迹球事件中包含曲线滑动操作,如果被测程序需要曲线滑动时可以选用此参数。可通过–pct- trackball参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Trackball (ACTION_MOVE): 0:(2.0,3.0)
:Sending Trackball (ACTION_MOVE): 0:(-1.0,4.0)
:Sending Trackball (ACTION_MOVE): 0:(2.0,-3.0)
该事件是由一系列的Trackball(ACTION_MOVE)事件组成的,观察手机上的操作,即为一系列的曲线滑动操作。
5.屏幕旋转事件
屏幕旋转事件是一个隐藏事件,在Android官方文档中并没有记录这个事件。它其实是模拟的Android手机的横屏和竖屏切换。可通过–pct- rotation参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending rotation degree=1, persist=false
:Sending rotation degree=3, persist=true
:Sending rotation degree=2, persist=true
:Sending rotation degree=0, persist=true
该事件由一个rotation事件组成,其中degree表示的是旋转方向,顺时针旋转,0表示旋转90度的方向,1表示旋转180度的方向,2表示旋转270度的方向,3表示旋转360度的方向。在执行过程中,可以看到手机屏幕在横竖屏之间不断地切换。
6.基本导航事件
基本导航事件是指点击方向输入设备的上、下、左、右按键的操作,现在手机上很少有上、下、左、右按键,这种事件一般用得比较少。可通过–pct- nav参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Key (ACTION_DOWN): 19 //KEYCODE_DPAD_UP
:Sending Key (ACTION_UP): 19 //KEYCODE_DPAD_UP
:Sending Key (ACTION_DOWN): 20 //KEYCODE_DPAD_DOWN
:Sending Key (ACTION_UP): 20 //KEYCODE_DPAD_DOWN
:Sending Key (ACTION_DOWN): 21 //KEYCODE_DPAD_LEFT
:Sending Key (ACTION_UP): 21 //KEYCODE_DPAD_LEFT
:Sending Key (ACTION_DOWN): 22 //KEYCODE_DPAD_RIGHT
:Sending Key (ACTION_UP): 22 //KEYCODE_DPAD_RIGHT
该事件是由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的,点击的就是上、下、左、右四个方向按键。
7.主要导航事件
主要导航事件是指点击“主要导航”按键的操作,这些按键通常会导致UI界面中的动作,如5-way键盘的中间键、回退按键、菜单按键。可通过–pct-majornav参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Key (ACTION_DOWN): 23 // KEYCODE_DPAD_CENTER
:Sending Key (ACTION_UP): 23 // KEYCODE_DPAD_CENTER
:Sending Key (ACTION_DOWN): 82 // KEYCODE_MENU
:Sending Key (ACTION_UP): 82 // KEYCODE_MENU
该事件是由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的,点击的按键就是中间键和菜单键。
8.系统按键事件
系统按键事件是指点击系统保留使用的按键的操作,如点击Home键、返回键、音量调节键等。可通过–pct-syskeys参数来配置其事件百分比。从Monkey执行该事件对外输出的日志可以看到:
:Sending Key (ACTION_DOWN): 5 //KEYCODE_CALL
:Sending Key (ACTION_UP): 5 //KEYCODE_CALL
:Sending Key (ACTION_DOWN): 4 //KEYCODE_BACK
:Sending Key (ACTION_UP): 4 //KEYCODE_BACK
:Sending Key (ACTION_DOWN): 3 //KEYCODE_HOME
:Sending Key (ACTION_UP): 3 //KEYCODE_HOME
:Sending Key (ACTION_DOWN): 24 //KEYCODE_VOLUME_UP
:Sending Key (ACTION_UP): 24 //KEYCODE_VOLUME_UP
:Sending Key (ACTION_DOWN): 25 //KEYCODE_VOLUME_DOWN
:Sending Key (ACTION_UP): 25 //KEYCODE_VOLUME_DOWN
该事件是由一个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)组成的,点击的按键就是其他的一些系统按键,如字母按键、数字按键等。因为现在手机很少带字母按键或数字按键,所以这个事件一般使用得比较少。
monkey 环境搭建
解了Monkey的参数及11大事件后,接下来让我们了解一下Monkey的运行环境搭建方法。Monkey是由adb命令来启动的,故只要配置好adb环境即可。
(1)下载并安装Android SDK和JDK。
(2)将Android SDK目录下的platform-tools和tools目录配置到系统环境变量Path中。
(3)在终端中输入adb( 输出如下指令表示Monkey环境配置成功)
Monkey启动
Monkey启动方式很简单:先连接被测手机到PC上,然后打开CMD命令行窗口输入对应的adb命令行即可。通过命令行启动Monkey有两种方式:
❑ 直接PC启动
adb shell monkey [options]
❑ Shell端启动
adb shell
monkey [options]
这两者的区别是,通过PC端启动,Monkey运行日志可以保存在PC上;通过Shell端启动,Monkey运行日志可以保存在手机里
注意
Monkey启动后会不断地向被测对象发送随机事件流,直到事件执行完毕或者发生异常时才停止。在Monkey运行过程中,即便断开与PC的连接,Monkey依然可以在手机上继续运行。
停止Monkey的方法是:直接杀掉手机上的Monkey进程。具体方法如下:
>adb shell ps | grep monkey
获取到com.android.commands.monkey的进程ID
adb shell kill pid
举例:adb shell kill 30898
通过kill命令杀死对应的Monkey进程。
下面来看一个最简单的Monkey命令行示例:
adb shell monkey -v 10
通过该命令启动Monkey后,Monkey向被测手机的Android系统发送10条随机事件流。当启动运行Monkey测试后,手机上会开始执行Monkey测试,同时在命令行窗口输出日志,执行完成后,可以看到日志信息。
monkey 测试方法
1.常规的稳定性测试
测试背景:被测程序是某公司的一个项目,希望通过monkey 来模拟用户长时间的随机操作,检查被测应用是否异常(应用奔溃或者无响应)
测试脚本
adb shell monkey -p com.xxx.xxx --pct-touch 40 --pct-motion 25 --pct-appswitch 10 --pct-rotation 5 -s 12358 --throttle 400 --ignore-crashes --ignore-timeouts -v 50000
显而易见,这个Monkey测试的命令相比上一节提到的要复杂得多,主要是对一些操作事件做了限制,从而减少了Monkey伪随机化的无效操作。这体现在以下几个方面。
1)使用-p参数来制定测试应用的包名(Package)
因为被测程序是一个特定的Android应用程序,需要指定被测程序的包名。指定包名后,Monkey会根据包名找到对应的应用,并启动其main activity,然后执行Monkey测试。
技巧
查找应用包名的方法有很多,这里简单列举几个常用方法:(1)通过pm命令查看。在命令行窗口输入:
>adb shell
>pm list package
此时将列出手机上所有的应用包名,在列表中找到要测试的应用包名即可。
(2)通过查看APK源码下的AndroidManifest.xml文件。(
(3)通过aapt命令查看。
(4)通过adb logcat抓取当前Android机运行的App的包名。
2)使用–pct-xxx参数限制Monkey执行的事件类型和占比
前面已经说了,这个测试的目的是希望模拟用户操作,因此需要让Monkey执行的事件尽可能地接近用户的常规操作,这样才可以最大限度地发现用户使用过程中可能出现的问题。因此需要对Monkey执行的事件百分比做一些调整。
触摸事件和手势事件是用户最常见的操作,所以通过–pct-touch和–pct-motion将这两个事件的占比调整到40%与25%;目标应用包含了多个Activity,为了能覆盖大部分的Activity,所以通过–pct-appswitch将Activity切换的事件占比调整到10%;被测应用之前在测试中出现过不少横竖屏之间切换的问题,这个场景也必须关注,因此通过–pct-rotation把横竖屏切换事件调整到10%。
3)使用-s参数来指定命令执行的seed值
Monkey会根据seed值来生成对应事件流,同一个seed生成的事件流是完全相同的。这里指定了seed值,是为了测试发现问题时,便于进行问题复现。
4)使用–throttle参数来控制Monkey每个操作之间的时间间隔
指定操作之间的时间间隔,一方面是希望能更接近用户的操作场景,正常用户操作都会有一定的时间间隔;另一方面也是不希望因为过于频繁的操作而导致系统崩溃,尤其是在比较低端的手机上执行测试时。因此通过–throttle设置Monkey每个操作固定延迟0.4秒。
5)使用–ignore-crash和–ignore-timeouts参数使Monkey遇到意外时能继续执行
在执行Monkey测试时,会因为应用的崩溃或没有响应而意外终止,所以需要在命令中增加限制参数–ignore-crash和–ignore-timeouts,让Monkey在遇到崩溃或没有响应的时候,能在日志中记录相关信息,并继续执行后续的测试。
6)使用-v指定log的详细级别
Monkey的日志输出有3个级别:默认的是level 0, -v -v日志级别为level 1, -v -v日志级别为level 2。日志的级别越高,其详细程度也越高。为了方便问题的定位,将日志级别设置为level2。
在常规的稳定性测试中,虽然可以自定义各种事件的操作占比,但毕竟是随机事件流。在实际测试过程中,难免会遇到Monkey点了我们不希望它点击的地方,比如误点了工具栏导致网络断开的情况等。当测试过程中Wi-Fi断开时,是否有可能自动重连呢?
2.自定义脚本的稳定性测试
常规Monkey测试执行的是随机的事件流,但如果只是想让Monkey测试某个特定场景(执行固定的事件流)呢?这时候就需要用到自定义脚本了,Monkey支持执行用户自定义脚本的测试,用户只需要按照Monkey脚本的规范编写好脚本,存放到手机上,启动Monkey通过-f scriptfile参数调用脚本即可。
Monkey自定义脚本的编写模板如代码清单
Monkey脚本常见API如下:
技巧
Monkey脚本只能通过坐标的方式来定位点击和移动事件的屏幕位置,这里就需要提前获取坐标信息。获取坐标信息的方法很多,最简单的方法就是打开手机中的开发人员选项,打开“显示指针位置”。随后,在屏幕上的每次操作,在导航栏上都会显示坐标信息。
下面来看一个简单的例子:这里要测试的是应用宝App,测试的操作是打开应用宝,点击输入框,输入“yyb”,点击搜索。搜索完成后,点击返回键返回应用宝首页。首先,将在本地编写需要的测试脚本命名为monkey.script(文件格式无要求),脚本内容如下代码
其次,将文件push到手机或模拟器的sdcard中:
> adb push monkey.script /sdcard/
最后,执行脚本:
adb shell monkey -f /sdcard/monkey.script -v 1
此时可以看到手机按照前面的脚本开始执行了
从手机上可以看到,Monkey按照脚本启动了应用宝,随后点击搜索框,输入“yyb”,点击搜索
窍门如果需要重复执行某个脚本,只要在Monkey启动命令中修改执行次数即可。例如:
adb shell monkey -f /sdcard/monkey.script -v 10
此时Monkey 会重复执行monkey.script 脚本10次
3.结合辅助命令,获取更多信息
常规测试只要记录下Monkey日志,再分析Monkey日志检查是否有异常即可。但是,很多时候,测试除了想知道执行过程是否有异常,还需要能获取执行过程中的一些详细信息或性能数据,比如想知道Monkey执行过程中是否存在内存泄漏,需要获取内存信息。这时候就需要借助一些辅助的命令来获取更多信息了。下面列举了几种Monkey测试中常用的辅助命令,使用方法也非常简单,只要在执行Monkey的同时,另起一个命令行窗口输入对应命令执行即可。
❑ 获取logcat日志信息:
adb shell logcat -v time>log.txt
❑ 获取内存信息:
adb shell dumpsys meminfo <进程名>
❑ 获取CPU消耗信息:
adb shell top -n 1 | find "进程名”
❑ 获取电量信息:
adb shell dumpsys battery
❑ 获取GPU信息:
adb shell dumpsys gfxinfo<进程名>
❑ 获取流量信息:
adb shell cat/proc/uid_stat/<被测应用的UID>/tcp_rcv
技巧
如何获取被测应用的UID
步骤1:查看被测应用的进程ID(PID)
adb shell ps | grep <被测应用包名>
步骤2:查看被测应用的用户ID(UID)
adb shell cat /proc/$pid/status
4.Monkey测试策略制定思路
前面介绍了几种常见的Monkey测试方法,但在实际项目中,选择哪种Monkey测试策略,则需要根据实际项目的情况来做判断。主要是看测试目的及被测应用自身的特点。假如我们想测试浏览器的双指缩放功能是否有异常,那就需要选择–pct-pinchzoom参数,调大双指缩放事件的占比进行Monkey测试;假如我们想验证ROM的横竖屏切换功能是否正常,那就需要选择–pct-rotation参数,调大横竖屏切换事件的占比进行Monkey测试;假如我们想验证重复某种特定操作时,应用是否会存在异常,那可以选择-f参数,自定义Monkey脚本进行验证;假如我们想验证长时间操作时应用是否会存在内存泄漏,那就需要结合-hprof参数和dumpsys meminfo<进程名>进行Monkey测试。总之,Monkey测试策略是需要依据测试目的和被测程序的特点来制定的。
monkey 日志分析
Monkey日志分析是Monkey测试中非常重要的一个环节,通过日志分析,可以获取当前测试对象在测试过程中是否会发生异常,以及发生的概率,同时还可以获取对应的错误信息,帮助开发定位和解决问题。介绍日志分析方法之前,先来看一下日志的保存方法。
1.Monkey日志的保存方法
Monkey运行日志常见的保存方法有三种:
❑ 保存在PC中,代码如下:
adb shell monkey [option] <count>d:\monkey.txt
执行以上命令,Monkey的运行日志将被保存在PC上的D盘下的一个monkey.txt文件中。
❑ 保存在手机中,代码如下:
>adb shell
>monkey [option] <count> /mnt/sdcard/monkey.txt
执行以上命令,Monkey的运行日志将被保存在手机中的SD卡上的一个monkey.txt文件中。
❑ 标注流与错误流分开保存,代码如下:
Monkey [option] <count> 1>/sdcrad/momkey.txt 2>/sdcard/error.txt
执行以上命令,Monkey的运行日志和异常日志将被分开保存。此时Monkey的运行日志将被保存在monkey.txt文件中,而异常日志将被保存在D盘下的error.txt中。执行结束后,可以看到SD卡上新增加了monkey.txt和error.txt。monkey.txt显示运行日志
如果Monkey执行期间存在Crash(崩溃)或ANR(Application Not Responding,应用程序无响应), error.txt中会显示错误日志
2.Monkey日志内容解析Monkey运行时输出的日志一般包含四类信息,分别是测试命令信息、伪随机事件流信息、异常信息、Monkey执行结果信息。
1)测试命令信息
Monkey启动后会输出当前所执行命令的各种参数信息,其中包括种子(Seed)信息、事件数量、可运行的应用列表以及各事件百分比等。这些信息都是通过Monkey命令参数所指定的,这部分日志信息的解析
2)伪随机事件流信息
当Monkey开始执行测试后,会顺序输出执行的事件流信息,主要是前面提到的11大事件。这部分日志信息的解析,
3)异常信息
当Monkey执行过程中遇到错误时,会输出对应异常信息
4)Monkey执行结果信息当Monkey执行完所有事件后,会输出执行结果信息,其中包括执行的事件数量、旋转的角度、丢失的事件数量、网络状态以及Monkey最终的执行结果
如果Monkey执行过程中出现了异常导致执行失败,会输出对应的执行失败的原因,第几个事件执行失败以及所使用的随机种子数
3.Monkey日志异常信息查找
Monkey执行过程中常见的错误类型主要有两类:应用程序无响应(ANR)和崩溃(Crash)。ANR是指当Android系统监测到应用程序在5秒内没有响应输入的事件或广播在10秒内没有执行完毕时抛出无响应提示
Crash是指当应用程序出现错误时导致程序异常停止或退出的情况,当出现Crash时通常会弹出对应的错误提示框如图
要统计Monkey日志中错误出现的次数也非常简单,只要搜索关键字“ANR”和“CRASH”出现的次数即可。由于通常Monkey测试的日志会比较大,日志内容也非常多,为了简化统计操作,可以使用bat脚本进行统计
最终执行后,在脚本目录下会生成Result.txt文件记录异常出现的次数
根据统计结果,可以得到Crash和ANR出现的次数,以及出现在哪个日志文件中,出现该错误的包名。如果需要更详细的错误信息,可以打开对应的Monkey日志文件查询。通过详细日志信息,测试可以定位到引起Crash的原因,以及出现Crash的代码行信息。这里给出常见的一些Crash错误信息
当获取到Crash和ANR日志信息后,理论上开发人员就可以开始根据日志内容分析和定位问题了。但事实上,要定位问题单靠日志信息还是非常困难的,有时候开发人员还需要知道问题复现的场景,同时增加更多的调试日志以协助定位。这时候,他们可能会期望测试能够复现问题或者提供问题出现场景和操作步骤。通常,测试人员可以通过使用同一个种子数(seed值),再次执行Monkey来尝试复现问题。这种方法比较费时,并且不是所有的随机Crash和ANR都可以通过这种方法来复现.
monkey 的基本原理
前面介绍了Monkey的各种参数以及其基本使用方法,顺带提到了在实践中Monkey页存在一定局限性,比如Monkey本身是不支持截图的,Monkey执行过程中网络断开后无法支持自动重连,等等。针对这些问题,可以通过改造Monkey源码的方式来实现
Monkey代码框架
前面说到了Monkey程序由一个名为“monkey”的shell脚本来启动执行,该脚本在Android文件系统中的存放路径是:/system/bin/monkey。这里简单解释一下此脚本,如下面的代码所示。
通过脚本不难发现,该批处理通过app_process命令指向的是手机上framework目录下的一个monkey.jar包中的“com.android.commands.monkey.Monkey”类(这个类即为Monkey的入口函数所在类)。monkey.jar的源码位于Android源码的“\development\cmds\monkey\src\com\android\commands\monkey”目录下
Monkey的代码框架如图
Monkey的代码核心模块主要包括主控、监控、事件源和事件四大部分:
❑ 主控模块:主控模块即Monkey类,是入口函数所在类,主要负责参数解析和赋值、初始化运行环境(导入package列表、检查内部配置、申请系统资源、初始化事件源、启动监控等)、执行runMonkeyCycles()方法针对不同的事件源开始获取并执行不同的事件。
❑ 监控模块:监控部分包括异常监控和网络监控两部分。异常监控通过ActivityWatch类来实现,主要监控Activity的Crash和ANR事件。网络监控通过MonkeyNetworkMonitor类来实现,主要用于统计运行期间移动网络和Wi-Fi网络的链接时长。
❑ 事件源模块:事件源代表不同的事件来源。以MonkeyEventSource为基类,衍生出三个Source类,分别代表网络来源(如Monkeyrunner)、随机事件来源(常规Monkey命令)、脚本来源(通过-f参数指定的脚本)三种事件来源。事件源要做的事情有:从对应来源获取信息,并生成对应的事件,将其插入事件队列中
❑ 事件模块:事件代表了各种用户操作类型。以MonkeyEvent为基类,衍生出各种Event类,每一个Event类代表一种用户操作类型,如常见的点击、输入、滑动事件等。MonkeyEvent抽象类中提供了intinjectEvent()方法,用于执行对应的事件。
Monkey代码逻辑详解
接下来看一下Monkey运行的流程。首先,从入口函数入手,Monkey.java的main方法如代码清单
从代码中可以看到main方法是通过调用run方法来运行Monkey的。Monkey.java的run方法核心处理代码如代码清单
从代码解析可以看到,run方法主要做了以下几件事。
❑ 参数解析和参数赋值。
❑ 申请系统资源。
❑ 初始化事件源。
❑ 启动网络监控程序。
❑ 调用runMonkeyCycles方法执行Monkey。
再来看一下runMonkeyCycles方法又做了什么。runMonkeyCycles中最核心的逻辑如代码
从代码分析可知,runMonkeyCycles做了两件事,一是调用了事件源的getNextEvent方法读取下一个事件,二是调用事件的injectEvent方法执行该事件。针对不同的事件源,getNextEvent方法的具体实现是不同的,不过它们的目的都只有一个,即获取下一个事件。以随机事件源(monkeySourceRandom)为例,来看一下它是怎么实现getNextEvent的,
monkeySourceRandom在初始化的时候会创建一个事件队列mQ用于存放Monkey事件。当外部调用getNextEvent方法时,会先去判断mQ队列中是否有事件,如果队列中没有事件,则调用generateEvents方法,根据事件源自身的规则生成事件流,并将其存放到mQ队列中;如果队列中有事件,则会取出第一个事件返回,并同时从队列中删除该事件。而获取事件后需要做的就是执行相应的事件,也就是将事件通过injectEvent方法逐一注入系统中。几乎每个Event事件都有对injectEvent方法的具体实现。这里以触摸事件(MonkeyTouchEvent)为例来看一下它是如何进行事件注入的
这里,执行事件通过调用WindowManager的接口来实现事件注入。最后来回顾一下整个Monkey事件执行的流程。
(1)初始化事件源,创建事件队列。
(2)通过getNextEvent()方法循环获取事件。
(3)通过injectEvent方法调用WindowManager的方法将事件注入系统中。
Monkey 扩展应用示例
前面讲到了Monkey的基本使用方法及其原理,可以看到Monkey功能还是很强大的,它不但可以进行随机测试,还可以执行指定脚本,结合adb命令还可以监控各种性能数据。但它毕竟是一款为稳定性测试而准备的小工具,所以存在很多局限性,正如前面Monkey实践一节中提到的,Monkey不提供截屏功能,因此测试很难找到问题复现的场景;Monkey无法进行控件识别,对事件流控制能力很微弱;执行过程中容易误点工具栏导致Wi-Fi关闭,影响测试效果;等等。下面介绍如何通过Monkey源码改造的方法来解决上述问题,以更好地提升Monkey的使用效果。
要改造Monkey,就要先了解如何重编译Monkey。
Monkey代码重编译执行方法
Monkey重编译的方法有两种,一种是在Linux环境下编译,另一种是在Windows环境下编译。因为在Windows环境下编译更为常见,所以这里会重点介绍第二种方法。
1.Linux环境下编译
在Linux环境下,下载要测试Android系统版本对应的全部源代码,进入源码目录。
步骤1:执行.build/envsetup.sh,设置Android的编译环境。
步骤2:执行make monkey开始编译Monkey。
编译成功后可在/out/target/product/generic/system/framework/中获取Monkey.jar包。
2.Windows环境下编译
Windows环境下的编译要稍微复杂一点。
步骤1:创建Monkey项目。同样也是需要下载要测试Android系统版本对应的全部源代码,在/development/cmds/monkey目录下找到Monkey的工程源码。在Eclipse中新建一个Java工程,把Monkey源码导入进去。
步骤2:设置Java Build Path。选中对应项目,在顶部菜单栏依次选题Project→Properties→Jave Build Path→Libraries,添加两个jar文件:android.jar和framework.jar。其中android. jar可以从Android Sdk中platforms\android-X\目录下获取;framework.jar可以通过以下两种方式获取。
(1)(推荐)从在Linux环境下Android源码根目录执行make update-api编译生成,如截图中的classes_dex2jar.jar文件就是通过Android源码编译生成的。
(2)直接从Android手机上/system/framework目录下获取已经编译好的framework.jar文件,把这个framework.jar解压,取出其dex,然后把它的dex通过dex2jar工具转换为jar包,导入工程。
添加android和framework的jar包后,还需要将framework的jar包顺序调整到顶部,如图所示。
步骤3:编译生成jar包。选择Monkey项目,单击右键→单击Export→选择输出的Jar包类型为“JAR file”类,单击“Next”按钮,
选择对应的构建工程,填写jar包输出路径,单击“Next”按钮,
进入打包选项页面,这里用默认选项即可,直接单击“Next”按钮
选择工程中main函数所在的类,单击“Finish”按钮,
编译完成后,在指定目录下就会生成对应的Monkey.jar包了。
步骤4:转换Monkey.jar包。Eclipse编译出来的jar包是不能直接放到Android手机上运行的,在Android上无法像Java中那样方便地动态加载jar。原因是:Android的虚拟机(Dalvik VM)是不能识别Java打出的jar的byte code的,这里需要通过Android sdk中的dx工具来优化转换成Dalvik byte code才行。
将打包好的jar复制到SDK安装目录android-sdk-windows\platform-tools下,打开命令行进入platform-tools目录,执行命令:
dx --dex --output=<生成的目标文件> <要装换的文件>
此时在android-sdk-windows\platform-tools目录下会生成最终的Monkey可执行包,这个jar包可直接在手机上运行。这里输出的是Monkeytest.jar。
3.重编译的包运行方法
假设前面重编译后生成的Monkey文件为Monkey.jar文件,测试如何在手机上执行这个新文件呢?要运行重编译后的Monkey.jar有以下两个前提条件。(由于不同版本的Android系统API不同,因此不同版本的Monkey包也是不能通用的。例如:Android 4.2版本的Monkey只能在Android4.2的系统上运行。)
❑ 手机拥有root权限。
❑ 手机Android版本与Monkey.jar包的Android版本一致
步骤1:创建启动shell脚本。
在本地新建一个用于启动Monkey的shell脚本,输入以下命令,并保存成Monkey。这个文件是用来启动和执行Monkey.jar的,如下面的代码所示。
步骤2:上传脚本和jar包到手机。
将步骤1创建的Monkey脚本和Monkey.jar包上传到手机的/data/loal目录(可自己定义,与shell脚本中的目录一致即可),并将Monkey文件修改成可执行权限,如下面代码所示。
个别手机上执行chmod命令时会报Segmentation Fault错误,这时可以先adb shell进入,通过sw root命令切换到root下,再执行chrnod 777/data/monkey即可。
步骤3:运行monkey。
通过命令行窗口,输入adb shell ./data/local/monkey [count]命令启动Monkey. jar包即可运行Monkey。
Monkey截图优化
掌握重编译Monkey的方法后,接下来要开始进行Monkey源码改造了。第一个改造就是截图改造。Monkey使用过程中最大的难题就是如何获取异常出现的场景。虽然Monkey在执行过程中提供了日志来记录事件执行顺序,但是光靠日志来定位异常出现的场景并复现它是非常困难的。当Monkey执行过程中出现异常时,若可以对应进行截图并记录异常出现前执行的操作,就可以清晰地知道异常出现的场景,也便于定位和解决问题。
具体改造方法如下:
测试期望实现的是在每个事件执行过程中增加截图并在图片上画出事件轨迹。这里以屏幕触摸操作为例,首先找到触摸事件所在的文件MonkeyMotionEvent.java,找到负责执行该事件的injectMotionEvent方法。先来看一下它的实现
这里是通过WindowManager的一个实例访问InjectPointerEvent和injectTrackballEvent接口来实现事件注入的。其中me参数表示要执行的一个MotionEvent事件。常规的触摸操作是由三类MotionEvent事件组成的,一类是ACTION_DOWN类型的MotionEvent,表示按下;一类是ACTION_MOVE类型的MotionEvent,表示移动;一类是ACTION_UP事件,表示抬起。ACTION_DOWN是每个点击事件的开始,只要在这个地方添加一个截图方法,即可对点击事件进行截图。
除了截图以外,还需要记录事件轨迹。因此在每个事件中,需要把点坐标记录下来,存放到一个PointList的点队列中。当出现ACTION_UP时,意味着整个点击操作已经完成了,此时把最后一个点坐标加到队列后,再把队列中保存的所有点坐标按一定的规则画到开始时截的图中,压缩图片并保存,此时就完成了一个点击操作的截图和记录。最后别忘了清空PointList队列。修改后的injectEvent方法如下
上面用到的截图方法TakeScreenshot是直接调用一个第三方的截屏工具gsnapCap进行截屏的
前面在injectEvent方法中调用了一个drawPoint方法将操作轨迹画到图片上,drawPoint方法具体实现如代码清单
参照上面的修改思路,将Monkey的其他方法也进行类似的修改。这样,Monkey每执行一个操作,系统就会自动对其进行截图描点了。由于手机SD卡空间有限,如果不断地往手机里添加文件,SD卡很快就会被填满。因此还需要增加一个定时清理的操作,即只保存最新的30张图片,一旦图片超过30张,就会把最早的一张图片删除。
前面做了这么多,最终当然是希望被测程序在跑Monkey出现Crash、ANR时,把截图保存下来。因此需要在程序中加入下面这几行代码,当出现Crash等异常时,会将最新的30张截图复制下来,存放到一个以“Crash+当前时间差”命名的文件夹中
这样Monkey截图功能就完成了,来看一下执行的效果。当手机执行完Monkey后,假如执行过程中出现了Crash或ANR,那么在sdcard的Monkey目录下会对应生成Crash和ANR目录,并保存发送异常之前的30张屏幕截图
图片上面会显示当前事件的操作坐标
Monkey Wi-Fi 自动重连优化
我们知道大部分的应用程序是需要联网的,假如Monkey在执行过程中Wi-Fi断开了怎么办?由于Monkey执行的是随机事件流,过程中的操作无法控制,用户很容易误点到工具栏而导致Wi-Fi断开。对于需要联网的应用,当Wi-Fi断开后,很多页面都会无法打开,此时Monkey执行的效果会相当不理想。相信这也是绝大多数用户遇到的问题,当前小节介绍的就是如何通过Monkey改造来实现Wi-Fi断开重连的功能。
首先,新增一个用于Wi-Fi监控的事件MonkeyWifiEvent。在Monkey中新增一类事件有以下两个步骤。
(1)在MonkeyEvent新增一个eventType类型
(2)新增对应事件的MonkeyWifiEvent类,需继承自MonkeyEvent类
从上面的代码可以看到,该事件是通过调用CheckWifiConnection()方法来检查Wi-Fi连接并自动重连的。CheckWifiConnection()方法的实现很简单,首先初始化一个WifiManager的对象,调用其getWifiEnabledState方法,检查当前Wi-Fi是否连接,当判断为Wi-Fi无连接时,调用setWifiEnabled方法打开Wi-Fi。等待Wi-Fi打开后,通过getConfiguredNetworks方法获取Wi-Fi列表,并遍历列表查找需要连接的Wi-Fi的SSID。查找到后,连接到对应的Wi-Fi上。具体实现如代码
前面说的需求是实现定时监控,所以需要在Monkey.java中的runMonkeyCycles()下每隔1000个事件就插入一个Wi-Fi监控事件
这样,当Monkey每执行完1000个事件后,就会去检测一下Wi-Fi的连接状态,当发现Wi-Fi断开就会自动重连。重新编译一下Monkey
Monkey扩展应用的优点和缺点
前面介绍了几个通过源码改造扩展使用Monkey的案例,这里来总结一下Monkey改造的优点和缺点。Monkey改造的优点非常明显:
❑ 不依赖PC机,是一种很好的单机自动化模式。
❑ 在Monkey内部可以调用系统底层接口,做到很多App做不到的事情。
❑ 基于Monkey源码的改造可以做很多个性化定制。
其缺点是:
❑ 被测手机最好是root手机。如前面所说,在执行Monkey的过程中需要对Monkey的jar包和shell脚本进行授权,如果不是root手机,这一步会非常麻烦。
❑ 不同Android版本的Monkey不通用,不利于做版本适配。因为Monkey是Android系统自带的,不同Android版本的Monkey的代码会存在差异,例如2.3版本的Monkey执行触摸方法时是通过调用IWindowManager的injectPointerEvent方法来实现的。
而4.0版本的Monkey则是通过调用InputManager 的injectInputEvent方法来实现的。
所以2.3版本的Monkey在4.0版本的Android手机上是运行不起来的。