1 什么是Monkey?
Monkey
是一个Android
平台自动化测试的一个工具,主要用来测试稳定性的;Monkey
可以模拟用户的一系列操作,比如触摸屏幕、滑动、按键等,对这些操作进行压力测试,检测程序多久会出现异常;Monkey
是Android
系统自带的,使用java
编写的;Monkey
的存放路径为:
/system/framework/monkey.jar
2 Monkey的启动
monkey.jar
启动使用shell
脚本,而shell
脚本存放的路径在:
/system/bin/monkey
- 启动有两种方式:
# 方式1(直接在PC上的命令行执行):
adb shell monkey {
参数}
# 方式2(进入设备执行):
进入到设备:adb shell
monkey {
参数}
- 比如可以查看
monkey
的参数:
adb shell monkey -help
C:\Users\Administrator>adb shell monkey -help
usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]
[-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]
[--ignore-crashes] [--ignore-timeouts]
[--ignore-security-exceptions]
[--monitor-native-crashes] [--ignore-native-crashes]
[--kill-process-after-error] [--hprof]
[--pct-touch PERCENT] [--pct-motion PERCENT]
[--pct-trackball PERCENT] [--pct-syskeys PERCENT]
[--pct-nav PERCENT] [--pct-majornav PERCENT]
[--pct-appswitch PERCENT] [--pct-flip PERCENT]
[--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]
[--pct-permission PERCENT]
[--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]
[--pkg-whitelist-file PACKAGE_WHITELIST_FILE]
[--wait-dbg] [--dbg-no-events]
[--setup scriptfile] [-f scriptfile [-f scriptfile] ...]
[--port port]
[-s SEED] [-v [-v] ...]
[--throttle MILLISEC] [--randomize-throttle]
[--profile-wait MILLISEC]
[--device-sleep-time MILLISEC]
[--randomize-script]
[--script-log]
[--bugreport]
[--periodic-bugreport]
[--permission-target-system]
COUNT
3 Monkey的工作原理是什么?
- 其工作原理是:利用
socket
通讯的方式来模拟用户操作,比如按键输入、触摸屏输入、手势输入等; - 再细化说(来源于网络总结):
1、
runMonkeyCyles()
获取事件,一个事件结束之后,通过调用getNextEvent()
方法获取下一个事件;
2、从事件队列里面获取事件,需要组建事件队列MonkeyEventQueue
,队列由MonkeyEvent
构成;
3、MonkeyEvent
通过command
设计模式将具体的事件(如:MonkeyActivityEvent
、MonkeyKeyEvent
、MonkeyMotionEvent
)通过injectEvent()
方法逐一注入到系统。
4 Monkey的使用方法
4.1 测试指定app
4.1.1 参数说明
- 使用
-p
参数即可; - 该参数主要是让
monkey
允许系统启动指定的app
; - 比如指定一个包,我们下载了一个支持安卓6.0的
qq
,安卓6.0QQ:4.1.2 错误排查
- 使用命令安装后,如果提示以下信息:
Failure [INSTALL_FAILED_NO_MATCHING_ABIS]
- 这是因为该
APP
和模拟器的CPU
架构不一样,我这里修改了下模拟器的CPU
架构为ARM
的,之前创建的是X86
架构的; - 换了新的架构后进入了系统,并且重新安装
apk
:
adb install QQv5.apk /data/local/tmp
F:\monkey_test>adb install ls /data/local/tmp
Performing Push Install
adb.exe: need APK file on command line
F:\monkey_test>adb install QQv5.apk /data/local/tmp
Performing Push Install
QQv5.apk: 1 file pushed, 0 skipped. 11.8 MB/s (22506899 bytes in 1.817s)
pkg: /data/local/tmp/QQv5.apk
ver: /data/local/tmp
Success
- 终于安装成功了,如下:
4.1.3 启动指定apk
- 使用以下命令查看下安装的路径以及包的名字为
com.tencent.mobileqqi
:
adb shell ls /data/data
com.android.smoketest
com.android.smoketest.tests
com.android.soundrecorder
com.android.statementservice
com.android.systemui
com.android.vpndialogs
com.android.wallpaper.livepicker
com.android.webview
com.android.widgetpreview
com.example.android.apis
com.example.android.livecubes
com.example.android.softkeyboard
com.svox.pico
com.tencent.mobileqqi
jp.co.omronsoft.openwnn
- 使用
monkey
启动com.tencent.mobileqqi
并做2个计数:
adb shell monkey -p com.tencent.mobileqqi 2
- 如果想使用
mokey
随机启动某个apk
并发送N个随机事件,使用:
adb shell monkey -p 包名 N
4.2 显示日志级别
4.2.1 参数说明
- 使用
-v
参数,可反馈日志级别; - 日志级别有三个:
级别 | 参数 | 说明 |
---|---|---|
Level0 |
-v |
启动、测试完成、最终结果信息 |
Level1 |
-v -v |
详细日志,每个Activity 事件信息 |
Level2 |
-v -v -v |
最详细日志 |
4.2.2 日志显示
Level0
日志信息:
F:\monkey_test>adb shell monkey -p com.tencent.mobileqqi -v 2
:Monkey: seed=1701146443923 count=2
:AllowPackage: com.tencent.mobileqqi
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
// Event percentages:
// 0: 15.0%
// 1: 10.0%
// 2: 2.0%
// 3: 15.0%
// 4: -0.0%
// 5: -0.0%
// 6: 25.0%
// 7: 15.0%
// 8: 2.0%
// 9: 2.0%
// 10: 1.0%
// 11: 13.0%
:Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity;end
// Allowing start of Intent {
act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity } in package com.tencent.mobileqqi
:Sending Touch (ACTION_DOWN): 0:(191.0,846.0)
Level1
日志信息:
F:\monkey_test>adb shell monkey -p com.tencent.mobileqqi -v -v 2
:Monkey: seed=1701146536977 count=2
:AllowPackage: com.tencent.mobileqqi
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
// Selecting main activities from category android.intent.category.LAUNCHER
// + Using main activity com.tencent.mobileqq.activity.SplashActivity (from package com.tencent.mobileqqi)
// Selecting main activities from category android.intent.category.MONKEY
// Seeded: 1701146536977
// Event percentages:
// 0: 15.0%
// 1: 10.0%
// 2: 2.0%
// 3: 15.0%
// 4: -0.0%
// 5: -0.0%
// 6: 25.0%
// 7: 15.0%
// 8: 2.0%
// 9: 2.0%
// 10: 1.0%
// 11: 13.0%
:Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity;end
// Allowing start of Intent {
act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity } in package com.tencent.mobileqqi
Sleeping for 0 milliseconds
:Sending Key (ACTION_DOWN): 23 // KEYCODE_DPAD_CENTER
// Allowing start of Intent {
cmp=com.tencent.mobileqqi/com.tencent.mobileqq.activity.LoginActivity } in package com.tencent.mobileqqi
// activityResuming(com.tencent.mobileqqi)
Events injected: 2
:Sending rotation degree=0, persist=false
:Dropped: keys=0 pointers=0 trackballs=0 flips=0 rotations=0
## Network stats: elapsed time=1915ms (0ms mobile, 0ms wifi, 1915ms not connected)
// Monkey finished
Level2
日志信息:
F:\monkey_test>adb shell monkey -p com.tencent.mobileqqi -v -v -v 2
:Monkey: seed=1701146594293 count=2
:AllowPackage: com.tencent.mobileqqi
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
// Selecting main activities from category android.intent.category.LAUNCHER
// - NOT USING main activity com.android.contacts.activities.PeopleActivity (from package com.android.contacts)
// - NOT USING main activity com.android.dialer.DialtactsActivity (from package com.android.dialer)
// - NOT USING main activity com.android.settings.Settings (from package com.android.settings)
// - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser)
// - NOT USING main activity com.android.calendar.AllInOneActivity (from package com.android.calendar)
// - NOT USING main activity com.android.deskclock.DeskClock (from package com.android.deskclock)
// - NOT USING main activity com.android.email.activity.Welcome (from package com.android.email)
// - NOT USING main activity com.android.camera.GalleryPicker (from package com.android.gallery)
// - NOT USING main activity com.android.camera.Camera (from package com.android.camera)
// - NOT USING main activity com.android.music.MusicBrowserActivity (from package com.android.music)
// - NOT USING main activity com.android.messaging.ui.conversationlist.ConversationListActivity (from package com.android.messaging)
// - NOT USING main activity com.example.android.apis.ApiDemos (from package com.example.android.apis)
// - NOT USING main activity com.android.gesture.builder.GestureBuilderActivity (from package com.android.gesture.builder)
// - NOT USING main activity com.android.widgetpreview.WidgetPreviewActivity (from package com.android.widgetpreview)
// - NOT USING main activity com.android.backuptester.MainActivity (from package com.android.backuptester)
// - NOT USING main activity com.android.calculator2.Calculator (from package com.android.calculator2)
// - NOT USING main activity com.android.customlocale2.CustomLocaleActivity (from package com.android.customlocale2)
// - NOT USING main activity com.android.development.Development (from package com.android.development)
// - NOT USING main activity android.app.AliasActivity (from package com.android.development_settings)
// - NOT USING main activity com.android.providers.downloads.ui.DownloadList (from package com.android.providers.downloads.ui)
// - NOT USING main activity com.android.quicksearchbox.SearchActivity (from package com.android.quicksearchbox)
// + Using main activity com.tencent.mobileqq.activity.SplashActivity (from package com.tencent.mobileqqi)
// Selecting main activities from category android.intent.category.MONKEY
// - NOT USING main activity com.android.launcher3.Launcher (from package com.android.launcher3)
// - NOT USING main activity com.android.settings.Settings$RunningServicesActivity (from package com.android.settings)
// - NOT USING main activity com.android.settings.Settings$StorageUseActivity (from package com.android.settings)
// Seeded: 1701146594293
// Event percentages:
// 0: 15.0%
// 1: 10.0%
// 2: 2.0%
// 3: 15.0%
// 4: -0.0%
// 5: -0.0%
// 6: 25.0%
// 7: 15.0%
// 8: 2.0%
// 9: 2.0%
// 10: 1.0%
// 11: 13.0%
:Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity;end
// Allowing start of Intent {
act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity } in package com.tencent.mobileqqi
Sleeping for 0 milliseconds
:Sending Trackball (ACTION_MOVE): 0:(0.0,-1.0)
// Allowing start of Intent {
cmp=com.tencent.mobileqqi/com.tencent.mobileqq.activity.LoginActivity } in package com.tencent.mobileqqi
// activityResuming(com.tencent.mobileqqi)
Events injected: 2
:Sending rotation degree=0, persist=false
:Dropped: keys=0 pointers=0 trackballs=0 flips=0 rotations=0
## Network stats: elapsed time=5630ms (0ms mobile, 0ms wifi, 5630ms not connected)
// Monkey finished
- 启动后的界面显示:
4.2.3 保存日志
- 使用重定向的方法可以将日志保存到指定的目录,比如当前目录下:
adb shell monkey -p com.tencent.mobileqqi -v -v -v 2 > ./monkey_qq.log
4.2.4 错误排查
- 关于
ANR
问题:日志中搜索ANR
即可; - 关于崩溃问题:日志中搜索
Exception
; - 正常执行:在
monkey
执行完后,在日志最后显示执行事件的次数和时间,且以monkey finished
结束,表示执行完成;4.3 跳过执行操作
4.3.1 忽略异常崩溃
- 使用
--ignore-crashes
参数可以忽略异常崩溃,这样monkey
会执行完所有事件;
adb shell monkey -p com.tencent.mobileqqi -v > --ignore-crashes 2
4.3.2 忽略超时
- 使用
--ignore-timeouts
可忽略程序超时,monkey
不会因超时而停止运行;
adb shell monkey -p com.tencent.mobileqqi -v > --ignore-timeouts 2
4.3.3 忽略许可错误
- 使用
--ignore-security-exceptions
可忽略许可错误,如证书、网络许可等;
adb shell monkey -p com.tencent.mobileqqi -v > --ignore-security-exceptions 2
4.4 事件参数
4.4.1 参数说明
以-
pct
开头控制事件百分比的参数,针对不通的测试类型可以调整百分比;有关事件参数说明如下(均表示百分比):
参数 | 说明 |
---|---|
--pct-touch PERCENT |
单击事件 |
--pct-motion PERCENT |
直线滑动事件 |
--pct-trackball PERCENT |
曲线滑动事件 |
--pct-syskeys PERCENT |
导航栏(Home 、Back 等) |
--pct-nav PERCENT |
导航事件(上下左右) |
--pct-majornav PERCENT |
导航事件(返回、确认、菜单) |
--pct-appswitch PERCENT |
Activity 的启动比率 |
--pct-flip PERCENT |
模拟器适用的事件 |
--pct-anyevent PERCENT |
不常用事件,比如按键 |
--pct-pinchzoom PERCENT |
多点手势缩放 |
--pct-permission PERCENT |
权限事件 |
4.4.2 指定事件百分比
- 直接在启动事件上加以上的参数即可;
- 参数后直接加上数值(表示百分数):
adb shell monkey --pct-motion 40 -p com.tencent.mobileqqi 2
4.5 事件延迟
- 使用
--throttle
可表示两个事件之间的延迟,单位ms
;
adb shell monkey --throttle 1000 -p com.tencent.mobileqqi 2
4.6 指定随机事件种子值
- 使用-s参数表示产生随机事件种子值:
test01: adb shell monkey -p com.tencent.mobileqqi -s 50 2
test02: adb shell monkey -p com.tencent.mobileqqi -s 50 2
4.7 终止进程
4.7.1 终止错误进程
- 使用
--kill-process-after-error
终止执行错误的进程。
4.7.2 监控崩溃事件
- 使用
--monitor-native-crashes
监控代码的崩溃事件。
4.7.3 停止monkey
- 使用
--wait-dbg
停止执行中的monkey
,直到有调试器相连为止。
5 一个实例
5.1 实例需求
- 启动qq;
- 实现3个事件操作;
- 时间间隔2s;
- 指定2个事件,占比为20%,30%;
- 忽略异常、崩溃、超时;
- 保存日志。
5.2 实例实现
- 参数如下:
adb shell monkey -p com.tencent.mobileqqi --throttle 2000 --pct-touch 20 --pct-motion 30 -v -v -v -s 50 --ignore-crashes --ignore-timeouts --ignore-security-exceptions 3 > ./test.log
:Monkey: seed=50 count=3
:AllowPackage: com.tencent.mobileqqi
:IncludeCategory: android.intent.category.LAUNCHER
:IncludeCategory: android.intent.category.MONKEY
// Selecting main activities from category android.intent.category.LAUNCHER
// - NOT USING main activity com.android.contacts.activities.PeopleActivity (from package com.android.contacts)
// - NOT USING main activity com.android.dialer.DialtactsActivity (from package com.android.dialer)
// - NOT USING main activity com.android.settings.Settings (from package com.android.settings)
// - NOT USING main activity com.android.browser.BrowserActivity (from package com.android.browser)
// - NOT USING main activity com.android.calendar.AllInOneActivity (from package com.android.calendar)
// - NOT USING main activity com.android.deskclock.DeskClock (from package com.android.deskclock)
// - NOT USING main activity com.android.email.activity.Welcome (from package com.android.email)
// - NOT USING main activity com.android.camera.GalleryPicker (from package com.android.gallery)
// - NOT USING main activity com.android.camera.Camera (from package com.android.camera)
// - NOT USING main activity com.android.music.MusicBrowserActivity (from package com.android.music)
// - NOT USING main activity com.android.messaging.ui.conversationlist.ConversationListActivity (from package com.android.messaging)
// - NOT USING main activity com.example.android.apis.ApiDemos (from package com.example.android.apis)
// - NOT USING main activity com.android.gesture.builder.GestureBuilderActivity (from package com.android.gesture.builder)
// - NOT USING main activity com.android.widgetpreview.WidgetPreviewActivity (from package com.android.widgetpreview)
// - NOT USING main activity com.android.backuptester.MainActivity (from package com.android.backuptester)
// - NOT USING main activity com.android.calculator2.Calculator (from package com.android.calculator2)
// - NOT USING main activity com.android.customlocale2.CustomLocaleActivity (from package com.android.customlocale2)
// - NOT USING main activity com.android.development.Development (from package com.android.development)
// - NOT USING main activity android.app.AliasActivity (from package com.android.development_settings)
// - NOT USING main activity com.android.providers.downloads.ui.DownloadList (from package com.android.providers.downloads.ui)
// - NOT USING main activity com.android.quicksearchbox.SearchActivity (from package com.android.quicksearchbox)
// + Using main activity com.tencent.mobileqq.activity.SplashActivity (from package com.tencent.mobileqqi)
// Selecting main activities from category android.intent.category.MONKEY
// - NOT USING main activity com.android.launcher3.Launcher (from package com.android.launcher3)
// - NOT USING main activity com.android.settings.Settings$RunningServicesActivity (from package com.android.settings)
// - NOT USING main activity com.android.settings.Settings$StorageUseActivity (from package com.android.settings)
// Seeded: 50
// Event percentages:
// 0: 20.0%
// 1: 30.0%
// 2: 1.3333334%
// 3: 10.0%
// 4: -0.0%
// 5: -0.0%
// 6: 16.666668%
// 7: 10.0%
// 8: 1.3333334%
// 9: 1.3333334%
// 10: 0.6666667%
// 11: 8.666667%
:Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity;end
// Allowing start of Intent {
act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.tencent.mobileqqi/com.tencent.mobileqq.activity.SplashActivity } in package com.tencent.mobileqqi
Sleeping for 2000 milliseconds
:Sending Trackball (ACTION_MOVE): 0:(-2.0,-3.0)
:Sending Trackball (ACTION_MOVE): 0:(-4.0,-4.0)
Events injected: 3
:Sending rotation degree=0, persist=false
:Dropped: keys=0 pointers=0 trackballs=0 flips=0 rotations=0
## Network stats: elapsed time=2732ms (0ms mobile, 0ms wifi, 2732ms not connected)
// Monkey finished