news 2026/10/1 9:47:34

Monkey测试实战指南:从原理到崩溃日志分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Monkey测试实战指南:从原理到崩溃日志分析

每次版本提测前,我都会拉出那只猴子来跑一晚上。对做移动端测试的朋友来说,Monkey测试几乎算是app稳定性验证的入门标配。你不需要写一条测试用例,不用搭复杂的测试框架,一条adb命令就能让它像发疯了一样在屏幕上乱点,专门替你找那些人工测试根本碰不到的偶现崩溃和卡死问题。

这篇文章把我这些年跑Monkey的完整经验整理出来,从工具原理、参数设计到日志分析、问题排查一次讲清楚,适合刚接触app自动化测试的新人,也能给正在搭稳定性回归方案的老手一些参考。我会结合真实执行过程中的踩坑记录,把那些文档里不会明说的细节一并交代。

1. Monkey测试到底在测什么:从“一只猴子”说起

1.1 随机操作背后的逻辑

Monkey是Android SDK自带的一个命令行工具,名字起得很直白:它就像一个喝多了的猴子,在手机上随机乱点、乱滑、乱按。工具会在你的app上生成海量的伪随机事件流,包括点击、滑动、轨迹球、系统按键、应用切换等等,目的就是用高强度、无规律的操作把app逼到极限,看它会不会崩溃、卡死或者内存溢出。

很多人第一次接触它会觉得这工具“太傻太暴力”,完全不懂业务逻辑,连退出按钮和删除按钮有什么区别都分不清。但恰恰是这个“傻”,让它在稳定性测试里不可替代。因为真实用户的行为本身就充满随机性,你永远想不到某个用户会在什么页面连点五下返回键,又在某个空列表疯狂下拉刷新。而Monkey通过大量事件注入,模拟的就是这种极端情况下的用户行为。

核心逻辑其实很简单:事件生成器会按照你指定的比例和种子值,随机产生一系列触摸、滑动和按键事件,并把它们依次发送给被测应用。工具本身不关心应用的正确性,不校验界面跳转是否符合预期,它只负责制造压力和记录执行结果。如果程序在这个过程中挂了,那就算测试未通过。

1.2 Monkey测试和常规自动化用例的差别

这一点我要特别拎出来说,因为很多人会把Monkey测试和UI自动化混为一谈。我自己带过的测试组里,几乎每个新手第一次接到Monkey任务都会问:“是不是要写脚本?”答案是:不用。

常规的app自动化测试,比如用Appium、UIAutomator写用例,本质上是“沿着预设路径验证功能”。你打开首页,输入账号密码,点击登录,断言是否进入主界面。这套逻辑适合做功能回归,效率高、问题定位快,但它有个致命缺陷:一切都在预期内。测试用例写得再全,也覆盖不到开发自己都没想到的边界情况。

而Monkey测试是反过来的。它没有预期路径,没有业务规则,甚至没有一个断言。它的目标非常纯粹:让被测app在随机、高强度操作下存活下来。一个正常的业务页面被连续切换、快速点击、中途杀进程,还能不能保持不崩溃?在低内存状态下频繁跳转,会不会OOM?这个问题的答案,用常规自动化用例很难量化,但Monkey可以在一晚上给出结果。

所以我现在管项目里的稳定性测试,一直是“两条腿走路”:常规自动化用例负责功能正确性,Monkey测试负责“乱拳打死老师傅”式的抗压验证。两者互补,缺一不可。

2. Monkey命令实战:先把猴子放出来

2.1 从一条基础命令说起

Monkey工具不需要单独安装,它内置于Android系统镜像中。只要手机连上电脑,开启USB调试模式,就能直接通过adb执行命令。我们先用一条最简单的命令跑一次:

adb shell monkey -p com.example.app 1000

这条命令的意思很直白:在指定的app(com.example.app)内随机执行1000个事件。如果你没有指定包名,Monkey会在整个系统范围内随机操作,那就会把手机里的所有应用都折腾一遍,甚至可能触碰到系统设置。所以实际测试中,我几乎一定带-p参数,把测试范围锁死在被测应用上。

执行完以后,终端会滑动输出一系列日志,以Events injected: 1000和Monkey finished结尾,说明这轮测试通过了。如果你看到** Monkey aborted due to error.,那就说明测试过程中发现了崩溃、ANR或者权限异常,后面我会专门讲怎么看这些日志。

这里有一个小细节:Monkey执行期间不需要你盯着手机,你完全可以让它自己跑着,过一段时间回来收结果。这也是它适合做稳定性回归的重要原因之一。

2.2 控制这只猴子:核心参数详解

基础命令能跑,但只能算“入门”。说到这儿,我得坦白一个经验:如果你只懂得跑裸命令,那这只猴子就完全不可控。默认情况下,Monkey生成的触摸、滑动、系统按键比例是它自己定的,可能你只想测业务页面内的点击,结果它时不时给你按一下Home键,把app切到后台去了。测试结果自然就失真了。

所以我平时使用Monkey,核心思路就是通过各种参数给它“立规矩”。下面这几组参数是我每次执行时都会重点关注的:

参数作用我的常用配置
-p指定被测应用包名,可多个被测app包名
-s设置种子值,复现同一事件序列固定一个整数,如-s 20241201
--throttle每个事件之间的延迟间隔(毫秒)300~500
--pct-touch触摸事件(点击)百分比30~40
--pct-motion滑动事件百分比20~30
--pct-majornav主要导航事件(如Back)百分比10~15
--pct-appswitch切换应用事件百分比5~10
--pct-syskeys系统按键(Home、音量键等)0~5
--pct-anyevent其他类型事件0~10
-v日志级别,-v -v -v最详细至少-v -v

一个完整的命令看起来长这样:

adb shell monkey -p com.example.app \ --throttle 300 \ -s 20241201 \ --pct-touch 35 \ --pct-motion 25 \ --pct-majornav 15 \ --pct-appswitch 10 \ --pct-syskeys 5 \ --pct-anyevent 10 \ -v -v \ 10000

这里每一个参数背后都有逻辑。--throttle控制在两个事件之间停多久,如果设置成0,Monkey会像机关枪一样疯狂输出事件,很容易把低端机的CPU打满,产生一些和“手势过快”相关的错乱;但真实用户的操作速度也没那么快,所以给一个300到500毫秒的缓冲更贴近实际场景。事件比例同理,--pct-touch调高就是让猴子集中在点击操作上,模拟用户主要用点按完成业务路径;--pct-appswitch调成10%,是为了模拟用户在app和其他应用之间来回切换的场景,这类操作对内存回收压力特别大,容易暴露内存泄漏问题。

2.3 种子值:让bug可以复现

在所有参数里,我认为最容易被忽略但最有价值的是-s种子值。

大家要理解一个关键点:Monkey生成的事件序列是伪随机,不是真随机。所谓伪随机,就是它虽然看起来乱,但本质上是由一个初始种子值按照固定算法推算出来的。换句话说,只要种子值相同、事件数相同、被测应用版本相同,Monkey就会生成一模一样的事件序列,连点哪个坐标都一致。

这个特性在定位bug时简直是救命稻草。比如昨晚Monkey跑崩了,日志里显示崩溃发生在第46320个事件。你想让开发帮忙看崩溃原因,开发肯定会问:“你能复现吗?”如果你没有用种子值,那真的很难复现,只能凭运气再乱跑一次。但你如果执行时带了-s 20241201,那很简单:

adb shell monkey -p com.example.app -s 20241201 --throttle 300 -v -v 100000 > monkey.log

再跑一次,它会在同一个事件点用同样的操作序列触发崩溃。开发只需要在这条命令上配合断点调试,就能当场抓到现场。

有一回我遇到一个概率极低的崩溃,只在特定页面连续多次滑动后偶现,人工复现了三天都没成功。后来Monkey一晚上跑出来,靠的就是种子值复现定位。所以我建议所有Monkey测试都固定种子值,并且把种子值记录到测试报告里,哪怕这轮测试全部通过也别删,以后想复现任何问题都能直接找它。

3. 从日志里捞出崩溃现场

3.1 Monkey日志能告诉我们什么

很多新手跑完Monkey,看到终端滚了一大堆日志就懵了,不知道该看哪里。其实Monkey的日志虽然多,但结构非常清晰。

正常情况下,日志会包含这样几个阶段:执行开始时,输出本次测试的配置信息,包括包名、种子值、事件数和各事件比例;执行过程中,根据-v的级别输出当前注入的事件类型和事件序号;执行结束时,输出统计信息,包括总事件数、注入的事件类型分布以及Monkey finished标记。

我习惯用重定向把日志完整保存下来:

adb shell monkey -p com.example.app -s 20241201 --throttle 300 -v -v 10000 > monkey_test.log 2>&1

保存以后,先在文件里搜几个关键标记:

  • 搜“Monkey finished”确认全程正常结束。
  • 搜“CRASH”定位崩溃导致的终止。
  • 搜“ANR”定位无响应问题。
  • 搜“aborted due to error”定位异常中断。

日志级别这块,-v越多信息越详细。平时回归我一般用-v -v,能看到每个事件的类型和序号,又不至于让日志文件膨胀到几百MB。如果需要精确定位崩溃前最后一个操作是什么,我会用-v -v -v执行一个短时间定位测试,信息量足够,日志大小也能接受。

3.2 读崩溃日志的实战套路

如果Monkey在运行过程中发现app崩溃,日志末尾通常会出现一段这样的内容:

// CRASH: com.example.app (pid 12345) Short Msg: java.lang.NullPointerException Long Msg: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.os.Bundle.getInt(java.lang.String)' on a null object reference Build Label: xxx

这段信息里的Short Msg和Long Msg已经能直接告诉我们是哪类异常、在哪个方法调到了空对象。但注意,Monkey自身日志里的堆栈信息通常很短,要拿到完整堆栈,还得配合抓取logcat。因为Monkey崩溃发生时,Java层异常堆栈同样会输出到logcat,而且带详细的行号方法名。

我的标准操作流程是这样的:先让Monkey正常跑着,同时在电脑上开一个终端持续抓取AndroidRuntime日志:

adb logcat -s AndroidRuntime:E > crash_error.log

等Monkey结束,打开crash_error.log,搜“FATAL EXCEPTION”,就能看到完整的异常线程堆栈。再顺着堆栈找自己业务代码的行号,把崩溃点定位到具体函数。

如果出现的是ANR(Application Not Responding),日志里一般会有ANR in com.example.app的标记,而且Monkey会因为超时中断。这种情况建议在崩溃前后各抓一次logcat,重点看main线程在等待什么操作。如果测试机有root权限,可以直接拉取/data/anr/下的trace文件分析;没root的机器,可以试试adb bugreport方式收集。

排查时间长了你会发现,Monkey日志的最大价值不是那个崩溃标记本身,而是它附近记录的最后一批事件,那些事件序列就是崩溃的触发路径。

4. 正经的Monkey测试方案长什么样

4.1 事件数和参数比例怎么定

接Monkey这个任务,新人最爱问的一句话是:“到底要跑多少次事件才算完?”我的回答是:先看你的测试目标和时间窗口。

如果是冒烟回归,目标只是验证本轮改动没有特别严重的稳定性问题,跑5000到10000个事件就够了,大概十几分钟出结果。这个档位适合每个迭代的提测阶段,快速圈定问题范围。

如果是常规稳定性测试,我一般跑到50000个事件左右,耗时约两三个小时,基本能覆盖大部分异常路径。如果项目版本临近发布,或者这个模块之前有过崩溃前科,我会直接拉一个过夜的稳定性测试,跑20万到50万事件。这里有一个需要特别注意的地方:事件数和--throttle共同决定总时长。假设你设置--throttle 300,每秒钟约注入3个事件,50万事件大约需要46个小时,这种强度通常周末或晚间跑比较合适。

参数比例方面,我常用的套路是根据业务类型做微调:

应用类型touchmotionmajornavappswitchsyskeysanyevent
电商购物类35251510510
工具效率类25202515510
视频影音类30351010510
游戏类453010555

这套比例不是官方标准,是我根据业务特点迭代出来的。电商类重点是商品列表和交易页面,点击和滑动节奏要接近真实用户;游戏类操作高频且集中在点击和滑动上,系统导航反而会打断沉浸式体验,所以比例设得很低。设置完之后,别忘了一个细节:检查所有比例加起来是否接近100%。如果加起来和100差太多,Monkey会随机分配剩余比例,这时候整个测试的事件分布就不可控了。

4.2 执行前环境准备和执行策略

Monkey测试虽然不用写用例,但执行前的环境准备一样不能马虎。我在团队里反复强调过一句话:测试结果失真,往往不是猴子的问题,而是环境的问题。

第一,保持屏幕常亮。默认情况下手机息屏后,Monkey会停止注入事件,导致测试白跑。执行前务必执行:

adb shell svc power stayon true

测试结束后再恢复:

adb shell svc power stayon false

第二,预先授予运行时权限。现在的app普遍有大量动态权限请求,比如定位、相机、存储。如果权限弹窗在Monkey执行过程中弹出来,猴子会无脑乱点,有可能点到“拒绝”,后边的业务流程直接走不下去。测试结果要么变成“没有崩溃但其实也什么都没测到”,要么因为弹窗拦截导致大量事件没有真正作用于业务页面。我习惯在跑Monkey前就把关键权限全部授权:

adb shell pm grant com.example.app android.permission.CAMERA adb shell pm grant com.example.app android.permission.ACCESS_FINE_LOCATION

第三,处理好登录态。很多app不登录就只能停留在引导页或者登录页,Monkey再怎么点也进不了核心业务。所以我一般会先手动登录好测试账号,再启动Monkey。这样猴子能真正深入到下单、支付、消息中心这些核心模块里。

第四,就是多包场景。如果你们产品和多个app有联动,也可以在一轮测试里指定多个包:

adb shell monkey -p com.example.app -p com.example.pay -p com.example.push -v 50000

防止Monkey乱跳到其他应用时,因为目标应用不在测试范围里而意外中断。

5. 常见问题与排查技巧实录

5.1 高频问题速查表

实话说,Monkey测试本身不复杂,复杂的是测试过程中遇到的那些“看起来跟Monkey无关但又直接影响结果”的问题。我把这些年高频遇到的问题整理成一个速查表,照着处理基本不会跑偏。

现象可能原因解决办法
Monkey跑着跑着停住,返回桌面系统按键比例过高,误触Home键调低--pct-syskeys,必要时设为0
日志显示“No activities found to run”包名写错,或app未安装用adb shell pm list packages确认包名
权限弹窗频繁出现,测试失真运行时权限未预授权执行前用pm grant批量授权
测试过程中屏幕自动熄灭系统休眠策略设置svc power stayon true
日志尾部出现“** Monkey aborted due to error.”但没有Java堆栈可能触发了系统保护或native崩溃抓logcat看DEBUG和libc输出
复测时用相同-s还是复现不了事件数不一致,或app版本有差异确认种子值、事件数、版本号三个条件一致
国产ROM跑Monkey被系统自动清理系统后台限制或省电策略锁定测试机后台,在设置里关闭该app的省电优化

有一条是很多测试新人最容易踩的坑:Monkey在测试过程中因为某些异常事件导致系统UI进程(比如SystemUI)无响应,整个手机看起来像死机了。这种情况不是被测app的崩溃,而是系统层面的问题。处理方法是先区分是系统进程还是应用进程,如果测试机本身低端且内存不足,建议换一台性能余量更大的机器,否则测试结果里会混入太多和业务无关的干扰项。

5.2 我的几个独家心得

最后分享几个在多次实战中总结的经验,这几个点官方文档里基本找不到,但对跑好Monkey测试挺关键。

第一个心得:不要把Monkey跑过一次、通过了就丢到一边。我的习惯是每个版本都跑Monkey,并且把种子值、事件数、测试时间、机器型号全部记录成一个固定的测试基线。等到下个版本再跑同样配置的Monkey,横向对比崩溃率和ANR次数。虽然Monkey是随机事件,但固定种子值和事件数后,不同版本之间的事件序列是一样的,这个对比就有意义。哪个版本稳定性出现劣化,一眼就能看出来。

第二个心得:高版本Android上遇到存储相关崩溃要格外重视。Android 10之后的存储权限策略收紧,很多老代码在读写文件时会悄悄崩溃。这种bug平时手工测试很难触发,反而被Monkey的随机事件频繁撞上。遇到这种问题不要觉得是猴子“点歪了”导致的无效崩溃,它恰恰是真实用户同样会遇到的高频问题。

第三个心得:Monkey跑完之后,别忘了清理现场。测试过程中调节音量、切换WiFi、打开蓝牙都是家常便饭,我见过程序跑完整个手机音量被调到最大,还自动连上了其他蓝牙设备。恢复现场不是洁癖,是为了不影响下一个测试任务的初始环境。

第四个心得我想特别说给测试新人:不要迷信Monkey的“未崩溃”结果。Monkey的长处是发现偶现崩溃,而不是验证功能正确性。即使Monkey十万事件全数通过,也说明不了业务逻辑没有错。它是一道“保底”的检查线,但绝不是唯一的质量防线。

最后再分享一个实用技巧:如果你要跑的Monkey事件数很大,建议分多个日志文件分段保存,别挤在一个文件里。用后台执行和按时间切片保存的方式,比如每小时一个log文件,这样一旦某个时段出了问题,我们能快速定位是事件区间的哪一段,排查起偶现问题会顺手很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 9:47:30

从零搭建AI工程:从模型训练到部署上线的完整实践指南

1. 从零开始搭建AI工程:先搞清楚它到底解决什么问题不少人一看到"AI工程"四个字,第一反应是"又要学一堆算法、调参、跑模型"。我最初也这么想,但真正把一个AI项目从想法推到上线之后才意识到,算法只是冰山一角…

作者头像 李华
网站建设 2026/10/1 9:46:55

ZCode 开源终端 AI 编程代理:核心功能、上手实战与选型指南

1. ZCode 是什么:一句话讲清楚最近一两天,技术群里聊得比较多的一个词就是“ZCode 开源了”。最早看到这个词的时候,我心里其实打了个问号。AI 编程这块竞争太热了,每个月都有新项目冒出来,名字里带 Code 的尤其多&…

作者头像 李华
网站建设 2026/10/1 9:46:09

OnlyOffice下载失败?Nginx反向代理5大配置陷阱详解

1. 问题本质:这不是OnlyOffice的错,是反向代理链路上的“信任断点”“OnlyOffice插件打开文档时提示下载失败”——这句话在运维群、开发论坛和客户支持工单里高频出现,但绝大多数人第一反应是去查OnlyOffice日志、重装镜像、甚至怀疑Java版本…

作者头像 李华
网站建设 2026/10/1 9:45:02

从零编写Nessus自定义扫描策略:插件集配置与性能调优实战

1. 为什么默认策略总是“差点意思”先聊个日常。干安全评估这几年,Nessus基本是随身工具了。但说实话,大部分人的用法就是装完开默认策略直接扫,出个报告就算交差。这个流程应付常规巡检没问题,真到实战项目里就捉襟见肘了。举几个…

作者头像 李华
网站建设 2026/10/1 9:43:26

数据库性能优化全路径:从索引设计到分库分表实战指南

做数据库性能优化这些年,我最常听到的一句话就是“系统越来越慢了,数据库顶不住了”。业务方催、老板催,开发和DBA互相甩锅,最后查下来十有八九不是数据库真的扛不住,而是索引没建对、SQL写得糙、或者架构本身就停在单…

作者头像 李华
网站建设 2026/10/1 9:42:24

基于SpringBoot的农业乡村振兴助农管理系统(源码+讲解视频+LW)

联系博主 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 …

作者头像 李华