Android Monkey压力测试脚本实战:从原理到自动化落地
2026/9/8 10:11:36 网站建设 项目流程

1. 写在前面:Monkey测试的定位和落地场景

做Android应用测试的,几乎都绕不过Monkey这个名字。它不用写用例、不用搭环境、不用录脚本,只要一条命令就能让设备自己“玩”起来,靠随机事件流去暴力验证应用的稳定性。很多人觉得Monkey就是闲着没事乱点一通,实际上,真正把它用好的团队,是在用Monkey做崩溃、ANR、内存异常、兼容性隐患的第一道防线。

按我的经验,Monkey最适合三类场景:第一,版本发布前的稳定性冒烟——新版本提测后,用Monkey跑一轮,能和自动化用例形成互补,随机事件覆盖的路径往往比人肉/脚本录制的路径更野;第二,持续集成流水线里的自动巡检——每天晚上在真机/模拟器上跑半小时Monkey,第二天早上看报告,防止回归引入的随机问题;第三,设备老化与长期稳定性验证——设备长时间待机、长时间高频操作后的表现,Monkey可以直接作为长期老化测试的驱动工具。

这篇博客我打算从一个可直接落地的Monkey压力测试脚本出发,把工具原理、参数选型、脚本实现、报告分析、CI集成这几个环节都拆开说清楚。内容主要面向有一定Android基础但还没系统搞过Monkey的测试开发同学,也适合想把手动稳定性测试升级为自动化流程的团队参考。这里所有操作都基于Android SDK自带的monkey命令,不涉及任何第三方闭源框架,完全可以在自己电脑上复现。

2. 彻底搞懂Monkey的核心机制

2.1 Monkey的定位:它是怎么产生压力的

Monkey是Android SDK里一个命令行工具,全称是UI/Application Exerciser Monkey。它运行在设备的shell层,通过向系统发送伪随机的用户事件流(点击、滑动、按键、缩放等)来对应用做压力测试。这里有个关键点——Monkey发送事件走的是系统级的InputManager,而不是通过Instrumentation/app进程注入。这意味着它模拟的是“用户在真实操作系统”里的行为,而不是在App内部走一套测试桩,所以对App外部环境的压力(系统广播、焦点切换、Activity启动竞争)也能覆盖到。

我们可以把Monkey想象成一个“失控用户”:他不知道你的App该点哪里,只会随机地碰屏幕、乱滑、乱按。但他有一个特点——速度快、连续性强,能在短时间内在各种边界条件下触发你自己都想不到的Bug。我见过几个真实案例:一个视频类App在播放页连续快速切换清晰度导致内存泄漏,一个电商App在购物车列表极速滚动后刷新闪退,都是靠Monkey先撞出来的。

2.2 Monkey的事件源:随机性从哪里来

Monkey的事件序列是由一个随机种子(seed)控制的。同一台设备、同一个seed、同一条命令,跑出来的事件序列是完全一致的。这是Monkey调试里最重要的一个机制:Bug复现靠它,回归验证也靠它。

事件流分了几大类:

  • 触摸事件(--pct-touch):单个手指的点击操作,对应绝大多数UI交互。
  • 手势事件(--pct-motion):单个手指的滑动,包含按下、移动、抬起,用来触发列表滚动、页面切换这类操作。
  • 缩放事件(--pct-pinchzoom):双指捏合,适合地图、图片、浏览器页面。
  • 轨迹球事件(--pct-trackball):模拟轨迹球设备产生的移动和点击,真机/模拟器上用得少,但框架层默认会有一些。
  • 系统按键事件(--pct-syskeys):Home、Back、音量键、菜单键等系统级按键,用来测试App在系统事件打断下的表现。
  • 启动Activity事件(--pct-appswitch):通过am start随机启动其他App,然后通过Main Activity再拉回来。
  • 其他事件(--pct-other):按键、按键重复等杂项。

每种事件类型都可以设置百分比,默认百分比由Monkey按经验值分配。如果我们不做任何设置,Monkey会混合发送所有类型的事件。实际使用中,针对不同的测试目标,事件比例应该差异化配置——稳定性测试主打触摸+手势,系统兼容性测试增加系统按键占比,多App协作场景增加appswitch占比。这部分我后面给脚本的时候会具体演示怎么配。

2.3 Monkey终止条件:它什么时候停下来

Monkey的终止分两类:正常终止异常终止

正常终止包括:跑完了设置的事件总数(-v日志会输出Monkey finished);连续发了多次(默认2次,可通过--throttle调节奏)的事件被--timeout或自定义监听完结;手动用Ctrl+C中断。

异常终止包括:发生崩溃(crash)ANR(Application Not Responding)致命异常(如系统级崩溃)。默认情况下,发生异常Monkey会直接停掉,不会继续往下跑。

这个“异常即停止”的机制很关键。它保证了出问题的时候,我们能拿到问题发生点的事件序列上下文,而不是让事件继续乱点污染现场。脚本里只要监控Monkey进程的返回值,就能知道测试是否通过。后面我会详细讲怎么解析Monkey的日志和返回值。

3. Monkey脚本的自动化架构设计

3.1 为什么光用命令行还不够

老实说,如果只是想在桌面上跑一条adb shell monkey -p com.demo.app 10000,那根本不需要写什么“脚本”——这是Monkey的入门用法,任何一个会配环境的人都能敲出来。但在真实的测试工程里,事情并没有这么简单。

第一个痛点是缺少环境准备:测试前要清缓存、杀进程、设置屏幕超时、保持CPU唤醒、锁定系统弹窗干扰等。第二个痛点是缺少用例组织:不同的模块、不同的App、不同的测试目标(崩溃测试 vs 长时间压力 vs 回归冒烟),命令参数是完全不同的,手敲容易出错。第三个痛点是缺少结果输出与归档:Monkey的日志散落在终端或者临时文件里,跑完就没了,没法沉淀成报表。第四个痛点是缺少失败后的现场采集:当Monkey跑挂的时候,我们需要快速拿到logcat、崩溃堆栈、ANR trace、截图,一个裸命令是做不到这些的。

所以“脚本化”的本质,是把Monkey从一条测试命令,升级成一个自动化测试接口

3.2 脚本整体架构:从入口到报告的一条龙设计

我设计的这套脚本有如下组件:

  1. 环境准备模块:检测设备连接、设置屏幕常亮、关闭动画、清理App状态。
  2. 参数解析模块:通过命令行参数指定包名、种子、事件数、事件比例、超时时间、monkey日志路径等。
  3. Monkey执行模块:构造常见的Monkey参数组合,支持标准跑法与自定义跑法。
  4. 异常监控模块:捕获Monkey退出码、关键字日志,判定测试结果。
  5. 现场采集模块:在失败时自动化抓取logcat、dumpsys、截图、ANR trace等。
  6. 报告输出模块:生成一份简洁的结果文件(文本/HTML),方便后续追溯。

这个架构不重,每个模块都很薄,但它把Monkey测试从“一次性脚本”变成了一个可持续演进的小工具。后面我贴代码的时候会逐个模块展开。

3.3 技术选型:为什么还用Shell而不是Python

你可能会问,现在做自动化测试不是都爱用Python吗?为什么我这里还在用Shell?

选Shell有几个现实原因。Monkey是命令行工具,Shell和它是同层语言,不需要额外的运行时解释器;Android设备连接和交互本来就是Shell+adb的组合,Shell是Android生态里最通用的胶水层;在Linux CI机器上,Shell也是默认解释器,不需要维护Python依赖。而且我们这套脚本的结构足够简单,它不需要做复杂的对象抽象和业务建模,用Shell完成顺序编排+条件判断+字符串处理恰恰是最省事的方式。

不过我得多说一句:如果你们项目已经重度使用Python,把执行部分和报告部分用Python重写完全没问题。Shell方案是我面向“最通用、最快速落地”场景给出的选择。脚本化的核心在于自动化编排能力,而不是某一种具体语言。

4. 核心脚本实现:从环境准备到报告生成

4.1 环境准备:先把设备状态“洗”干净

Monkey测试前,设备状态必须可控。我这里给出一个经验脚本片段:

#!/bin/bash # prepare_env.sh - Monkey测试前置环境准备 # 用法: bash prepare_env.sh <serial> <package> SERIAL=$1 PACKAGE=$2 ADB="adb -s $SERIAL" # 1. 确认设备在线 if ! $ADB get-state >/dev/null 2>&1; then echo "[ERROR] 设备 $SERIAL 未连接" exit 1 fi # 2. 亮屏与解锁(部分设备需要先按电源键) $ADB shell input keyevent KEYCODE_WAKEUP sleep 1 $ADB shell wm dismiss-keyguard # 3. 屏幕常亮 $ADB shell settings put system screen_off_timeout 1800000 # 4. 关闭系统动画(减少干扰) $ADB shell settings put global window_animation_scale 0 $ADB shell settings put global transition_animation_scale 0 $ADB shell settings put global animator_duration_scale 0 # 5. 强停目标App并清理数据 $ADB shell am force-stop $PACKAGE $ADB shell pm clear $PACKAGE # 6. 关闭可能弹窗的系统组件(不同ROM差异较大,按需调节) $ADB shell settings put global heads_up_notifications_enabled 0 echo "[INFO] 环境准备完成"

有几个点我要单独拎出来讲,都是踩过坑的。

第2步的屏幕解锁。很多测试机开了锁屏密码或图案锁,Monkey事件根本进不到桌面。用wm dismiss-keyguard这个命令可以绕过锁屏,但注意如果你的设备开了PIN/密码,这个命令不一定有效,最稳妥的办法是准备测试机时在开发者选项里禁用锁屏。否则一旦屏幕锁住,Monkey就白跑了。

第3步的屏幕超时。默认的30秒超时很容易导致测试中途熄屏,一旦熄屏,事件只是打在黑屏上,压力效果直接归零。设置成比较长的时间(甚至不锁屏)是必须的。

第4步的关闭动画。很多小白不会做这一步。Monkey是高速连续事件的,如果系统还在执行动画,事件到达的坐标和当前界面可能会错位,导致一些莫名其妙的假失败。关了动画后,事件处理延迟会明显降低,测试结果更接近“用户真实快速操作”的场景。

4.2 Monkey参数选型:一套能打的标准组合

先祭出我最常用的一套“稳定性标准跑法”:

adb shell monkey \ -p $PACKAGE \ -s $SEED \ --throttle 300 \ --pct-touch 40 \ --pct-motion 25 \ --pct-pinchzoom 5 \ --pct-trackball 0 \ --pct-syskeys 10 \ --pct-appswitch 10 \ --pct-other 10 \ --ignore-crashes \ --ignore-timeouts \ --ignore-security-exceptions \ --monitor-native-crashes \ --kill-process-after-error \ -v -v -v \ $EVENT_COUNT

逐条解释下关键参数,并说说它们为什么这么配。

-p指定包名。只能指定一个包名。注意这里说的是“只锁目标包”,Monkey允许随机启动其他App,但如果指定了-p,Monkey会限制事件序列只发到该包对应的Activity上(系统级事件除外)。如果你希望测多App协作场景,可以用多个-p,但大多数稳定性测试建议只锁一个包,减少变量。

-s指定种子。我建议每次测试都手动指定一个唯一种子,并把它记录下来。为什么?因为它能让同一条命令重跑出完全一样的事件序列。这有两层价值:一是复现问题,开发说“我这边复现不了”的时候,你丢给他同一个种子的事件记录,他就可以在本地复现;二是回归验证,修完Bug后用同一个种子重跑,可以确认修复前后行为差异。

--throttle 300是事件间隔毫秒数,我推荐300~500。它表示每个事件之间等待300ms。太短会导致事件还在处理、下一个就来了,可能出现误报;太长会拖慢测试速度。300这个值是很多大厂在实践里的经验值,具备一定的真实性。如果你要模拟“极快操作”,可以设100,但误报率会上升;如果你要“长时间稳定操作”,可以设500。

--ignore-crashes--ignore-timeouts--ignore-security-exceptions这三个参数很关键。默认情况下Monkey遇到crash、ANR、安全异常会终止。加了忽略项之后,Monkey会继续跑完整个事件序列,中途发生的错误会记录在日志里,但不会中断。

有同学会问:崩溃都不终止,那测试还有什么意义?

我的理解是:需要分场景。作为发布前的崩溃拦截,我倾向于不忽略(用默认模式),出现crash就立刻停止,方便快速定位;作为长时间老化测试,我倾向于忽略,这样即使偶尔一次闪退,后续事件还能继续压测其他功能点,测试结束后统一分析问题列表。一句话:如果目标是“刷Bug”,不忽略;如果目标是“长时间压测”,忽略。

--monitor-native-crashes这个参数用来监控native层的崩溃(比如C++代码里的段错误、SIGSEGV)。很多测试同学只看Java层崩溃,忽略native崩溃,实际上Android App常见的闪退里,native层崩溃占比相当高(尤其是音视频、图像处理类应用)。强烈建议开启。

--kill-process-after-error很实用。当发生错误(ANR/crash)后,Monkey会把这个出错的进程杀掉,防止后续事件继续对这个僵尸状态进程做无效操作。没有这个参数,后续事件可能都打在已经崩溃的进程上,导致大量串联误报。

-v -v -v是日志级别,三个v表示输出最详细的日志,包括事件序列、Activity跳转记录、错误日志。我用三v,因为定位问题的时候越详细越好。

再强调一个容易出错的点:事件数(EVENT_COUNT)要多少才合适。我给一个经验值:冒烟级3000~5000,标准级10000~20000,长时间老化50000~100000。10000个事件配--throttle 300,大概耗时50分钟到1小时。这个耗时通常是可以接受的。

4.3 Monkey执行与现场采集:崩溃后的“黄金1分钟”

Monkey跑挂之后,现场信息采集得越快越好,否则很多证据会被系统的垃圾回收机制吞掉。下面这段脚本展示了如何捕获异常并采集现场:

#!/bin/bash # run_monkey.sh - 执行Monkey并捕获现场 # 用法: bash run_monkey.sh <serial> <package> <seed> <events> <logdir> SERIAL=$1 PACKAGE=$2 SEED=$3 EVENTS=$4 LOGDIR=$5 ADB="adb -s $SERIAL" mkdir -p $LOGDIR # 清空logcat缓存,保证采集从Monkey开始 $ADB logcat -c # 开启后台logcat采集(按时间戳切分,避免日志过大) $ADB logcat -v threadtime > $LOGDIR/logcat_monkey.log 2>&1 & LOGCAT_PID=$! # 执行Monkey,记录退出码 $ADB shell monkey \ -p $PACKAGE \ -s $SEED \ --throttle 300 \ --pct-touch 40 \ --pct-motion 25 \ --pct-syskeys 10 \ --pct-appswitch 10 \ --pct-other 15 \ --ignore-crashes \ --ignore-timeouts \ --monitor-native-crashes \ --kill-process-after-error \ -v -v -v \ $EVENTS > $LOGDIR/monkey_output.log 2>&1 MONKEY_RC=$? # 停止logcat kill $LOGCAT_PID 2>/dev/null # 分析Monkey结果 if grep -q "CRASH" $LOGDIR/monkey_output.log; then echo "[CRASH] 检测到Java崩溃" grep -n "CRASH\|FATAL" $LOGDIR/monkey_output.log | head -20 fi if grep -q "ANR" $LOGDIR/monkey_output.log; then echo "[ANR] 检测到ANR" grep -n "ANR" $LOGDIR/monkey_output.log | head -20 fi if grep -q "Monkey finished" $LOGDIR/monkey_output.log; then echo "[PASS] Monkey执行完成" exit 0 else echo "[FAIL] Monkey异常中断" exit 1 fi

这段脚本里有几个细节我想详细说下。

关于logcat的采集。Monkey测试期间整个系统会产生大量日志,如果不清理就抓,拿到手的logcat文件里混杂了开机以来的碎片信息,非常难筛。所以执行前需要logcat -c清空缓冲区,然后再后台抓取线程级log。为什么加-v threadtime?因为它会输出每个日志行对应的进程ID、线程ID和时间戳,方便开发定位是哪个线程出了问题。

关于ANR现场的采集。发生ANR时,系统会生成/data/anr/traces.txt(不同Android版本路径可能有差异,高版本可能是/data/anr/下的多个trace目录)。这个文件普通应用读不到,需要root权限。如果你的测试机是userdebug固件且开了root,建议在Monkey之后加一句:

$ADB shell "cp /data/anr/* /sdcard/anr_backup/" 2>/dev/null

普通生产固件读不到也没关系,logcat里同样能抓到ANR的堆栈片段。

关于Monkey退出码。这里有个容易坑你的细节:Monkey进程本身如果遇到crash/ANR,adb shell的返回值不一定是非0,很多设备上它依然返回0。所以脚本里我推荐用关键字检测(grep "Monkey finished")来判断结果,而不是只看进程退出码。这也是我踩过的一个坑,早期脚本判断只认RC,导致crash了还报PASS,直到后来改成关键字匹配才准确。

4.4 参数扩展:针对不同场景的变体配置

前面的脚本是通用版本,实际项目里建议针对不同场景准备几套参数组合,系统化地组织测试策略:

场景建议参数组合用途与适用阶段
发布前崩溃拦截不忽略crash/ANR,事件数5000,throttle 200快速发现可复现的稳定性问题,适合开发自测
晚间已规划回归忽略crash/timeout,事件数10000,seed固定持续集成中固定每晚触发,给出稳定对比基线
老化压力测试忽略全部异常,事件数50000+,throttle 300长时间/高负载下的稳定性、内存泄漏等摸底
多应用交互场景不加-p或配置多个-p,增加syskeys/appswitch比例测系统级竞争、后台被杀、焦点切换对目标App的影响
特定场景专注增加--pct-touch--pct-motion占比针对已知风险区域做定向强化,例如某个页面滚动卡顿

这里核心思路是:没有万能参数,只有针对目标场景的参数编排。在自动化脚本里,可以将这套组合做成配置文件,让CI根据分支/阶段自动选择,而不是所有任务都跑同一条命令。

5. 脚本技术的几个进阶用法

5.1 用Shell脚本做循环批量测试

稳定性问题往往有概率性,一次跑过不代表十次都能过。建议用一个外层循环脚本把Monkey重复跑N轮,每轮换一个随机种子,收集多轮结果来做稳定性评估:

#!/bin/bash # loop_monkey.sh - 连续N轮Monkey测试,每轮独立seed SERIAL=$1 PACKAGE=$2 ROUNDS=$3 LOGDIR=$4 for ((i=1; i<=ROUNDS; i++)); do SEED=$RANDOM echo "===== 第 $i 轮测试,seed=$SEED =====" bash run_monkey.sh $SERIAL $PACKAGE $SEED 10000 $LOGDIR/round_${i} done

这里用$RANDOM每次生成随机种子。注意$RANDOM的范围是0~32767,如果怕种子重复,可以用时间戳+随机数的组合:

SEED=$(date +%s)$RANDOM

多轮循环的价值在于能算出崩溃率:比如10轮里挂了3轮,崩溃率就是30%。这个指标非常直接,适合做版本对比测试,用来判断当前版本的质量有没有改善。

5.2 时序报告的聚合:从日志中提取关键字段

Monkey跑完后的原始日志非常大,不看筛选直接丢给人是没法用的。可以写一段解析逻辑,从日志里提取关键信息:

# analyze_monkey.sh - 从monkey_output.log提取关键摘要 LOG=$1 echo "===== Monkey结果摘要 =====" echo "执行状态: $(grep -c 'Monkey finished' $LOG) 次完成" echo "--- 崩溃信息 ---" grep "CRASH" $LOG | awk '{for(i=1;i<=NF;i++) if($i=="Proc") print $(i+2)}' | sort | uniq -c echo "--- ANR信息 ---" grep -A2 "ANR in" $LOG echo "--- 事件比例统计 ---" grep "Events injected" $LOG

这段脚本做得比较粗糙,但思路是对的:先用grep/awk快速提炼崩溃归属进程和次数,再手动或自动去logcat里追具体堆栈。在CI里,这些摘要会拼成邮件或IM通知的文本,方便团队一眼看到问题概览。

5.3 定时任务与CI集成:测试不再靠人肉触发

Monkey要真正产生价值,建议把它接到定时任务或者CI流水线里。

Linux定时任务(crontab)示例:每天晚上10点自动跑一轮老化稳定性测试。

0 22 * * * /data/monkey_test/run_monkey.sh emulator-5554 com.demo.app 12345 20000 /data/monkey_test/reports/$(date +\%Y\%m\%d_\%H\%M\%S)

Jenkins流水线阶段示例:

stage('Monkey稳定性测试') { steps { sh ''' source /etc/profile adb start-server adb wait-for-device bash run_monkey.sh ${SERIAL} ${PACKAGE} ${BUILD_NUMBER} 10000 ${WORKSPACE}/monkey_reports ''' } }

把种子设成${BUILD_NUMBER}有个好处:每个构建的Monkey序列是可复现且唯一的。如果这次构建挂了,维护人员拿到构建号,就能反查出当时的seed,配合日志快速定位。

5.4 关于设备老化测试脚本的补充

热词里出现了“设备老化测试全自动执行脚本”,这个方向和Monkey脚本是强相关的。老化测试核心有三件事:长时间运行自动监控自动恢复。Monkey脚本可以做其中“长时间运行+自动监控”的部分。建议老化测试脚本再加一个“应用拉活”逻辑——Monkey结束后没必要非要杀掉进程或者清理数据,直接再拉起一轮即可。另外老化测试建议加--pct-appswitch比例到20%~30%,模拟用户在多个App间来回切换的真实使用习惯,能暴露更多系统资源竞争问题。不过请注意,老化测试跑出来的崩溃,对分析的要求更高,通常需要配合系统层面的监控(如内存水位、CPU占用、温度检测)一起看,否则单看crash日志很难区分是预埋的问题还是老化叠加导致的。

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

6.1 Monkey跑着跑着突然不动了

这种情况我们叫“Monkey假死”。常见原因有两个:第一个是事件进入了某个无响应的弹窗对话框,挡住了后续事件流,Monkey在等待窗口关闭,但弹窗本身没有超时机制,就卡住了。排查办法是抓取当前的UI层级:adb shell uiautomator dump /sdcard/ui.xml,把当前窗口的控件信息导出,就能看到是哪层窗口挡住了。

第二个原因是应用进入了死循环或单线程卡死,Monkey的事件发出去之后,App主线程没有及时处理,导致后续事件堆积。这种情况下dumpsys input能看到输入队列里的延迟情况。

建议在脚本里加一个--timeout参数(Monkey选项里有--timeout),超过设定时间没有收到事件响应就自动终止,避免CI流水线被一个假死卡到超时。

6.2 Monkey跑了很长时间没有崩溃,但App的使用体验明显变差了

这种情况大概率是内存泄漏堆积。Monkey一般只关注crash和ANR,但UI层面可能已经卡顿、掉帧、无响应。排查命令组合如下:

# 获取目标进程的内存信息 adb shell dumpsys meminfo $PACKAGE # 多次采样后对比PSS值 adb shell "top -n 5 -d 2 | grep $PACKAGE" # 查看是否出现OutOfMemory相关日志 adb logcat -d | grep -i "OutOfMemory\|lowmemorykiller\|LMK"

如果PSS值随时间单调递增,基本可以判定存在泄漏。在这个基础上配合monkey测试过程中定期的内存快照(比如每5000个事件用dumpsys抓一次),能画出内存趋势图,给开发提供非常有说服力的证据。

6.3--pct-appswitch设置了但看不到切换效果

经常有人问:我明明设置了--pct-appswitch比例,为什么日志里几乎没有Activity切换?

原因在于--pct-appswitch事件只能在Monkey随机选中启动器时生效,同时事件的具体行为还会被--pct的随机性和系统策略影响。想要稳定观测App切换效果,可以写一个单独的、高比例的配置:

adb shell monkey -p $PACKAGE --pct-appswitch 100 --pct-syskeys 0 --pct-touch 0 --pct-motion 0 100

这样能保证所有事件都是App切换行为。真实压测中通常不会为了固定某个effect而设成100%,但特殊场景下允许这么做。推荐如果做系统级稳定性测试,可以将appswitch设置在10%~20%之间,让Monkey在“专注测试目标App”的同时,保留一部分系统级跳转压力。

6.4 日志里出现// CRASH: com.demo.app (pid 12345)怎么快速定位

看到这个关键字后,不要只盯着Monkey的日志看,去logcat里搜同一个pid的堆栈。用绝对时间拼接:

adb logcat -d -v threadtime | grep "pid 12345" | tail -100

通常能看到类似FATAL EXCEPTION: mainjava.lang.NullPointerException的Java崩溃,也有*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***这种native崩溃头部标记。Java崩溃直接看后面的堆栈行;native崩溃则需要再抓一份tombstone,命令是:

adb shell ls /data/tombstones # 需要root或者userdebug固件 adb shell cat /data/tombstones/tombstone_00

tombstone里有完整的native线程状态和寄存器信息,对C++层的Bug定位帮助非常大。这也是压测脚本里**强烈建议开启--monitor-native-crashes**的原因——这是最容易被忽略、却又经常出大问题的层面。

6.5 设备上跑Monkey很卡,事件被大量丢弃

这种情况常见于低端设备或长时间跑测试后系统负载升高。看两个指标:CPU负载和输入延迟。

adb shell "top -n 1 | head -20" adb shell dumpsys input | grep "WaitQueue"

处理办法:适当增加--throttle到500或800,降低事件密度;测试机尽量选择中高配置的专用设备;每跑2~3轮重启一次设备,释放系统缓存。很多团队忽视测试机的硬件一致性,导致Monkey测试结果在不同机器上差异巨大,这一点在测试机选型时就要统一标准。

7. 结果分析与后续动作

7.1 崩溃分类的标准:不是所有崩溃都算稳定性事故

Monkey跑完,光看“有没有崩溃”是不够的,还要评估崩溃的严重级别

我的分法是:第一类,主流程崩溃,比如用户从启动页进入首页这一步就崩了,这种只要出现一次,版本就必须打回;第二类,局部功能崩溃,比如某个二级页、某个特定交互下崩了,一般对应特定Bug,评估优先级修复;第三类,偶发未知崩溃,只在特定设备/特定环境下出现,可能是兼容性问题,需要进一步确认影响面。

这个分级不是为了甩锅,而是为了让自动化测试结果能直接驱动开发排期——不能把一个偶发的第三方SDK崩溃和App主流程崩溃混为一谈。

7.2 把Monkey测试结果沉淀成团队资产

自动化测试最高级的目标,不只是“发现Bug”,而是“积累基线”。我建议团队每周固定跑一轮标准配置的Monkey,并维护一个稳定性趋势表:记录每轮的事件数、种子、崩溃数、ANR数、失败原因。坚持一段时间后,你会发现能够回答很多管理层关心的问题:“这个版本稳定性相比上个版本改进没有”“线上崩溃率有没有降低趋势”。

同时把每个崩溃对应的seed和堆栈归档到Bug管理系统,关联到开发的任务单里。这样下次回归测试时,直接取同一个seed重跑,可以快速验证修复效果。这就是Monkey能带给团队的长期价值——一个低成本的持续稳定性度量器。

8. 最后再分享一个小技巧

所有Monkey相关的脚本,强烈建议在.gitignore里忽略生成日志和报告目录,不要让几千行的logcat文件进Git仓库。另外在脚本开头统一加一行set -e反而要谨慎——我被它坑过,因为crash并不会让Shell退出,但网络断连会让adb命令返回非0,一旦set -e开着,整个流程会提前中断。脚本类工具更适合“显式判断并处理”,而不是依赖全局开关。

还有,如果你在Windows上跑这套Shell脚本,需要先装好Git Bash或者WSL环境,纯cmd下跑会有很多转义和路径问题。建议直接在Linux/Mac机器上跑,或者跑在CI服务器上,省心很多。

Monkey这种工具,看起来简单,真正用起来深度并不浅。它的上限取决于你对系统机制的理解和对测试场景的拆解能力。希望这篇脚本实战能帮你把Monkey从“随手敲的命令”变成一套真正可靠、可持续跑、能驱动质量改进的稳定性测试方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询