ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

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这条命令的意思很直白在指定的appcom.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控制在两个事件之间停多久如果设置成0Monkey会像机关枪一样疯狂输出事件很容易把低端机的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 21保存以后先在文件里搜几个关键标记搜“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”就能看到完整的异常线程堆栈。再顺着堆栈找自己业务代码的行号把崩溃点定位到具体函数。如果出现的是ANRApplication 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文件这样一旦某个时段出了问题我们能快速定位是事件区间的哪一段排查起偶现问题会顺手很多。
返回列表