一、前言

● Monkey是Android SDK提供的一个命令行工具,可以简单方便的发送伪随机的用户事件流,对Android APP做稳定性(压力)测试。
● 我们主要是为了测试app是否存在无响应和崩溃闪退等异常情况。
● 获取测试日志的ANR、CRASH、Exception信息,提供给开发人员分析和定位问题。
● 一般一次Monkey测试0 ANR 、0 CRASH是测试标准。
● 所有提交的应用,都需要经过应用商店的稳定性测试(如小米应用商店)——非功能性测试,即Monkey测试和遍历性测试。

二、基本操作流

● 安装adb,安卓手机开启USB调试,并成功连接设备
● 执行monkey测试命令,获取测试日志
● 查找ANR、CRASH、Exception信息,定位问题
● 开发修复问题后,用相同seed值测试确保问题修复

1、ADB搭建

此处只列举主要步骤

1.1、下载ADB命令行工具adb kit

下载地址: https://adbshell.com/downloads

得到adb.zip(仅含三个文件:adb.exeAdbWinApi.dllAdbWinUsbApi.dll),将该三个文件直接拷贝至C:\Windows\System32C:\Windows\SysWOW64

## 以下即为完成
adb --version
Android Debug Bridge version 1.0.41
Version 29.0.5-5949299
Installed as C:\Windows\system32\adb.exe

1.2、下载ADB驱动ADB Driver Installer

当android手机连接通过USB连接电脑时,经常碰到找不到驱动,无法识别设备。通常遇到这种情况我们需要下载相应的驱动安装包,安装驱动。

安装方法:

● 手机或android设备连接至电脑(本机模拟器需要:adb connect 127.0.0.1:62001)
● 双击ADBDriverInstaller.exe
● 界面将显示未安装ABD驱动的设备名
● 点击install完成安装

2、完整测试命令分析

可通过adb shell monkey -help查看使用参数

2.1 完整测试命令

主要使用命令:

参考小米开放平台的monkey参数:https://dev.mi.com/console/doc/detail?pId=43

# 根据小米开放平台测试参数修改而来的,实际使用中的测试参数
adb shell monkey -p com.xxx.consumer --pct-touch 40 --pct-motion 25 --pct-nav 5 --pct-majornav 15 --pct-appswitch 5 --pct-anyevent 5 --pct-trackball 0 --pct-syskeys 0 -s 123456 --throttle 300 --ignore-crashes --ignore-timeouts -v -v 1000 1>D:\Monkeytest\xxxAppMonkeyReport.txt 2>D:\Monkeytest\xxxAppMonkeyErrorReport.txt 
# 小米开放平台的测试参数
adb shell monkey -v --throttle 300 --pct-touch 30 --pct-motion 20 --pct-nav 20 --pct-majornav 15 --pct-appswitch 5 --pct-anyevent 5 --pct-trackball 0 --pct-syskeys 0 -p '%s' 1000
# 中断测试
# shell 9173 3896 4466296 110492 futex_wait_queue_me 0 S com.android.commands.monkey
adb shell ps | find "monkey"
adb shell kill "9173"

如果存在多个devices,指定一个设备如adb -s 2390d880 shell再执行monkey命令

2.2 指定测试App包名,-p参数

● 使用-p参数来制定测试应用的包名(Package)
● 获取手机端当前打开App的信息(如获取包名类名)adb shell dumpsys window |findstr mCurrent。如包名:com.xxx.consumer

# 获取命令:获取手机端当前打开App的信息
adb shell dumpsys window |findstr mCurrent

# 其中包名/类名 com.xxx.consumer/com.xxx.consumer.ui.activity.TabMainAct
 mCurrentUserId=0
      mCurrentUserId=0
  mCurrentFocus=Window{e41c87f mode=1 rootTaskId=2322 u0 com.xxx.consumer/com.xxx.consumer.ui.activity.TabMainAct}
    mCurrent=[0,137][1440,3200]
    mCurrentAppOrientation=SCREEN_ORIENTATION_PORTRAIT
      mCurrentRotation=ROTATION_0

2.3、限制Monkey执行的事件类型和占比,–pct-xxx参数

事件名称事件参数事件说明
触摸事件--pct-touch是指在屏幕某处按下并抬起的操作类似于点击操作,由一组Touch(ACTION_DOWN)和Touch(ACTION_UP)事件组成。
手势事件--pct-motion是指在屏幕某处的直线滑动操作,由一个ACTION_DOWN事件、一系列ACTION_MOVE事件和一个ACTION_UP事件组成
轨迹球事件--pct-trackball模拟轨迹球的操作,现在的手机几乎都没有轨迹球
缩放事件--pct-pinchzoom指放大缩小手势操作
屏幕旋转事件--pct-rotation模拟的Android手机的横屏和竖屏切换,是一个隐藏事件
基本导航事件--pct-nav是指点击方向输入设备的上、下、左、右按键的操作,现在手机上很少有上、下、左、右按键,这种事件一般用得比较少。
主要导航事件--pct-majornav点击“主要导航”按键的操作,这些按键通常会导致UI界面中的动作,如键盘的中间键、回退按键、菜单按键
系统事件--pct-syskeys点击系统使用的按键的操作,如点击Home键、返回键、音量调节键
启动事件--pct-appswitch在手机上启动一个Activity的操作,日志中开头:Switch: #Intent; action=android.intent.action.MAIN;
键盘事件--pct-flip键盘事件主要是一些与键盘相关的操作。比如点击输入框、键盘弹起、点击输入框以外区域、键盘收回等
其他类型事件--pct-anyevent其他类型事件包括了除前面提到的10种事件外其他所有的事件

2.4、指定命令执行的seed值,-s参数

Monkey会根据seed值来生成对应事件流,同一个seed生成的事件流是完全相同的。指定了seed值,可以在测试发现问题时,方便进行问题复现,相同的seed值,两次Monkey测试所产生的事件序列也相同的。不指定的话会自动分配。

2.5、控制Monkey每个操作之间的时间间隔,–throttle参数

--throttle 400

指定操作之间的时间间隔,单位是ms,可根据实际需求修改。

● 更接近用户实际的使用操作场景,正常用户操作都会有一定的时间间隔;
● 不希望因为过于频繁的操作而导致系统崩溃,在一些配置比较低的手机上执行测试时。因此通过–throttle设置Monkey每个操作固定延迟0.4秒。

2.6、Monkey遇到异常时能继续执行,–ignore-crash和–ignore-timeouts参数

在执行Monkey测试时,会因为应用的崩溃或没有响应而意外终止,可以在命令中增加限制参数–ignore-crash和–ignore-timeouts,使得Monkey在遇到崩溃或没有响应的时候,能在日志中记录相关信息,并继续执行后续的测试。

2.7、指定log的详细级别,-v参数

Monkey的日志输出分为3个级别(-v之间需要空格分开):

● Level 0(缺省值):除启动提示、测试完成和最终结果之外,提供较少信息。
● Level 1(-v -v):提供较为详细的测试信息,如逐个发送到Activity的事件。
● Level 2(-v -v -v):提供更加详细的设置信息,如测试中被选中的或未被选中的Activity等。

日志的级别越高,其详细程度也越高。为了方便问题的定位,可以将日志级别设置为level2。

2.8、最后是模拟执行事件次数,和指定生成日志文件目录

5000 > D:\GeelyAppMonkeyTest.log

如设置为模拟事件执行次数为5000,目录为D:\GeelyAppMonkeyTest.log

执行时间:经实际测试事件次数5000,操作间隔时间0.4s的情况下耗时约0.4s*5000=2000s

3、日志分析(关注异常信息)

Monkey运行时输出的日志一般包含四类信息,分别是测试命令信息、伪随机事件流信息、异常信息、Monkey执行结果信息。

3.1、测试命令信息

● 测试命令信息
● 随机种子值,执行事件数量
:Monkey: seed=12358 count=50000
● 可运行的应用列表
:AllowPackage: com.tencent.android.qqdownloader
● Category包含LAUNCHER和MONKEY
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
● 各事件的百分比
--pct-touch 70 触摸事件百分比70
--pct-motion 5 手势事件百分比70
--pct-trackball 5 轨迹球事件百分比10
--pct-pinchzoom 2 缩放事件百分比2
--pct-rotation 0 屏幕旋转事件百分比0
--pct-nav 0 基本导航事件百分比0
--pct-majornav 5 主要导航事件百分比5
--pct-syskeys 5 系统事件百分比 5
--pct-appswitch 2 Activity启动事件百分比2
--pct-flip 2 键盘翻转事件百分比
--pct-anyevent 2 其他事件百分比

bash arg: -p
  bash arg: com.geely.consumer
  bash arg: --pct-touch
  bash arg: 40
  bash arg: --pct-motion
  bash arg: 25
  bash arg: --pct-appswitch
  bash arg: 10
  bash arg: --pct-rotation
  bash arg: 5
  bash arg: -s
  bash arg: 12358
  bash arg: --throttle
  bash arg: 400
  bash arg: --ignore-crashes
  bash arg: --ignore-timeouts
  bash arg: -vv
  bash arg: 50000
:Monkey: seed=12358 count=50000
:AllowPackage: com.geely.consumer
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
// Event percentages:
//   0: 40.0%
//   1: 25.0%
//   2: 0.5479452%
//   3: 4.109589%
//   4: 5.0%
//   5: -0.0%
//   6: 6.849315%
//   7: 4.109589%
//   8: 0.5479452%
//   9: 10.0%
//   10: 0.2739726%
//   11: 3.5616438%

3.2、伪随机事件流信息

当Monkey开始执行测试后,会顺序输出执行的事件流信息,如:

#执行的事件流信息
#启动App事件
Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.xx.consumer/com.xxx.market.deploy.activity.DevContainActivity;end
    // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.consumer/com.geely.market.deploy.activity.DevContainActivity } in package com.xxx.consumer
    // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xxx.consumer/com.geely.market.deploy.activity.DevContainActivity } in package com.xxx.consumer
#点击事件
:Sending Touch (ACTION_DOWN): 0:(308.0,2215.0)

3.3、异常信息查找,日志搜索ANR、CRASH、Exception

当Monkey执行过程中遇到错误时,会输出对应异常信息,关键字查询:应用程序无响应(ANR)崩溃(CRASH)其他问题(Exception)强制退出(force closed)

● ANR:指当Android系统监测到应用程序在5秒内没有响应输入的事件或广播在10秒内没有执行完毕时抛出无响应弹窗提示
● CRASH:应用程序出现错误时导致程序崩溃、异常停止或退出的情况
● Exception:其他问题的一些问题
● 强制退出:在日志中搜索 force closed

当获取到CRASH和ANR日志信息后,理论上开发人员就可以开始根据日志内容分析和定位问题了。

如果标准流和错误流分开保存的话则直接查看错误流文件即可,如:xxxAppMonkeyErrorReport.txt

CRASH的错误日志如下:

// CRASH: com.xxx.consumer (pid 24358)
// Short Msg: kotlin.KotlinNullPointerException
// Long Msg: kotlin.KotlinNullPointerException
// Build Label: Xiaomi/venus/venus:11/RKQ1.200928.002/V12.5.15.0.RKBCNXM:user/release-keys
// Build Changelist: V12.5.15.0.RKBCNXM
// Build Time: 1635085758000
// kotlin.KotlinNullPointerException
//     at com.xxx.consumer.circle.ui.fragment.AllCircleFragment$initView$3.onLoadMore(AllCircleFragment.kt:116)
//     at com.scwang.smart.refresh.layout.SmartRefreshLayout$5.run(SmartRefreshLayout.java:1727)
//     at android.os.Handler.handleCallback(Handler.java:938)
//     at android.os.Handler.dispatchMessage(Handler.java:99)
//     at android.os.Looper.loop(Looper.java:233)
//     at android.app.ActivityThread.main(ActivityThread.java:7892)
//     at java.lang.reflect.Method.invoke(Native Method)
//     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
//     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
//

3.4、重现问题

● 找到Monkey哪个部分有问题
● 查看Monkey里面出错前的一些事件动作,并手动执行该动作
● 如果不能重现,执行之前的语句,seed值要相同

三、目前实际使用遇到的问题

1、小米手机跑Monkey调起app后不再继续运行且报错

错误代码:Injecting to another application requires INJECT_EVENTS permission

问题解决方案:

● 小米手机有两个USB调试开关,需要开启USB调试(安全设置)开关,且开启时要求设备安装了SIM卡才能成功开启。
● 设置入口:设置—> 更多设置—> 开发者选项 —>USB调试(安全设置)

2、手机在跑Monkey时无法观察到点击了何处和滑动的轨迹

实现方案:

● 小米手机有两个USB调试开关,需要开启USB调试(安全设置)开关,且开启时要求设备安装了SIM卡才能成功开启。
● 设置入口:设置—> 更多设置—> 开发者选项 —>打开显示点按操作反馈、指针位置开关

3、小米机型出现theme_compatibility.xml报错

猜测可能和系统主题有关,但是此处并非APP的报错,似乎可以忽略该问题

java.io.FileNotFoundException: /data/system/theme_config/theme_compatibility.xml: open failed: ENOENT (No such file or directory)
    at libcore.io.IoBridge.open(IoBridge.java:492)
    at java.io.FileInputStream.<init>(FileInputStream.java:160)
    at java.io.FileInputStream.<init>(FileInputStream.java:115)
    at java.io.FileReader.<init>(FileReader.java:58)
    at miui.content.res.ThemeCompatibilityLoader.getVersion(ThemeCompatibilityLoader.java:108)

4、实际使用中一直需要连着电脑操作,是否可以不连电脑。

解决方式:通过以下新增可执行的bat文件,pc启动后即可断开usb,日志按照日期建文件夹存到手机上

set folderName=%date:~0,4%%date:~5,2%%date:~8,2%
set fileName=%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%
adb shell mkdir -p /storage/emulated/0/Monkey/log/%folderName%
adb shell "monkey -p com.xxx.consumer --pct-touch 60 --pct-motion 30 --pct-nav 0 --pct-majornav 0 --pct-appswitch 5 --pct-anyevent 5 --pct-trackball 0 --pct-syskeys 0 -s 123456 --throttle 300 --ignore-crashes --ignore-timeouts -v -v 250 1>/storage/emulated/0/Monkey/log/%folderName%/xxxAppMonkeyReport%fileName%.txt 2>/storage/emulated/0/Monkey/log/%folderName%/xxxAppMonkeyErrorReport%fileName%.txt" &

n、完善中待续。。。