欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android自动化测试工具——Monkey

程序员文章站 2022-07-12 18:39:39
...

Android自动化测试工具——Monkey

上一周的软件测试课刚刚练习过移动应用的测试,通过Android SDK中的uiautomatorviewer(一个由Android提供的、用来扫描和分析Android应用程序的UI组件的GUI工具)来获取界面中的一个元素,然后通过点击按钮,读取、修改文本等来模拟真实用户和应用之间的交互。今天来说说另一个工界和学界中常用的Android测试工具——Monkey


Monkey简介

Monkey测试是Android平台下自动化测试的一种快速有效的手段,可运行在模拟器和实体设备上。通过Monkey工具可以模拟用户触摸屏幕、滑动轨迹球、按键等操作来对模拟器或者手机设备上的软件进行压力测试,检测该软件的稳定性、健壮性。它的原理是向系统发送伪随机的用户事件流(如按键输入、触摸输入、手势输入等),实现对正在开发的应用程序进行压力测试。因为这个工具在App中乱按、乱摸、乱滚、乱跳,像一个调皮的猴子,所以被称为Monkey。

Monkey工具的特点

  • 1.测试对象为程序包,有一定局限性
  • 2.使用的事件流数据流是随机的,不能自定义
  • 3.可对事件数量、类型、频率设置

补充:MonkeyRunner官方文档的网址
http://developer.android.com/guide/developing/tools/monkeyrunner_concepts.html

Monkey的基本语法

参数名 基本功能
-p 参数-p用于约束限制,用此参数指定一个或多个包(Package,即App)。指定包之后,Monkey将只允许系统启动指定的App。如果不指定包,Monkey将允许系统启动设备中的所有APP。
-v 用于指定反馈信息级别(信息级别就是日志的详细程度),总共分3个级别:level0-2
-s <\seed> 用于指定伪随机数生成器的seed值,如果seed相同,则两次Monkey测试产生的时间序列也相同。
-throttle <毫秒> 用于指定用户操作(即事件)间的时延,单位是毫秒
–ignore-crashes 用于指定当应用程序奔溃时(Force & Close错误),Monkey是否停止运行。如果使用此参数,即使应用程序奔溃,Monkey依然会发送事件,直到事件计数完成。
–ignore-timeouts 用于指定当前应用程序发生ANR(Application No Responding)错误时,Monkey是否停止运行,如果使用此参数,即使应用程序发生ANR错误,Monkey依然会发送事件,直到事件计数完成。
–ignore-security-exceptions 用于指定当程序发生许可错误时(如证书许可,网络许可等),Monkey是否停止运行。如果使用此参数,即使应用程序发送许可错误,Monkey依然会发送事件,直到事件计数完成
–kill-process-after-error 用于指定应用程序发生错误时,是否停止其运行。如果指定此参数,当应用程序发生错误时,应用程序停止运行并保持在当前状态(注意:应用程序仅是静止在发生错误时的状态,系统并不会结束该应用程序的进程)。
–monitor-native-crashes 用于指定是否监视并报告应用程序发生崩溃的本地代码
–pct- {+事件类别} {+事件类别百分比} 用于指定每种类别事件的数目百分比(在Monkey事件序列中,该类事件数据占总事件数目的百分比)
–pct-touch {+百分比} 调整触摸时间的百分比(触摸事件是一个down-up事件,它发生在屏幕上的某个单一位置)
–pct-motion 调整动作事件的百分比(动作事件由屏幕上某处的一个down事件、一系列的伪随机事件和一个up事件组成)
–pct-trackball {+百分比 } 调整轨迹事件的百分比(轨迹事件由一个或者几个随机的移动组成,有时还伴随有点击)
–pct-nav 调整“基本”导航事件的百分比(导航事件由来自方向输入设备up/down/left/right组成)
–pct-majornav {+百分比} 调整“主要”导航事件的百分比(这些导航事件通常引发图形界面中的动作,如:5-way键盘的中间按键、回退按键、菜单按键)
–pct-syskeys {+百分比} 调整“系统”按键事件的百分比(这些按键通常被保留,由系统使用,如Home、Back、Start Call、End Call及音量
–pct-appswitch {+百分比} 调整启动Activity的百分比。在随机间隔里,Monkey将执行一个startActivity()调用,作为最大程度覆盖包中全部Activity
–pct-anyevent {+百分比} 调整其它类型事件的百分比。它包罗了所有其它类型的事件,如:按键、其它不正常的设备按钮,等等
–wait-dbg 停止执行中的Monkey,直到有调试器和它相连接。

(!!!注:这里由于博客编辑器的原因,“-s ”的参数seed前面加了一个“\”,正常情况下没有这个符号。)

Monkey测试示例图解

我选择的app是开源中国(上周测试考试用到的apk)

(1)将apk安装到模拟器中。安装后,能够在模拟器中看到安装好的apk,如下图:
Android自动化测试工具——Monkey

(2)安装好apk文件后,需要知道应用程序主Activity(第一个启动的Activity)所在的包名。

获取包名有多种方法,可以参照这个网址中提到的指令。
http://www.51testing.com/html/93/136593-3714533.html

这里得到的包名如下图:
Android自动化测试工具——Monkey

(3) 使用moneky命令进行自动化压力测试。

命令为:

#monkey -p net.oschina.app.improve -v 100
//(参数说明:-p后跟软件所在包名,-v后跟测试的次数)。这里对该软件发送了100次的伪随机事件,即进行了100次的自动化测试。

(4) 查看测试的结果。
我的测试结果如下图:
Android自动化测试工具——Monkey
如果测试没有通过,会报错。如果输出了“Monkey Finished”则说明测试通过,这里的测试通过。

但是查看结果还是有不明白的地方,如Event Percentages的0-10各代表什么?
这一点,我们可以查看android-4.2.2_r1.2中的MonkeySourceRandom.java中定义的这些值:

public static final int FACTOR_TOUCH        = 0;  
public static final int FACTOR_MOTION       = 1;  
public static final int FACTOR_PINCHZOOM    = 2;  
public static final int FACTOR_TRACKBALL    = 3;  
public static final int FACTOR_ROTATION     = 4;  
public static final int FACTOR_NAV          = 5;  
public static final int FACTOR_MAJORNAV     = 6;  
public static final int FACTOR_SYSOPS       = 7;  
public static final int FACTOR_APPSWITCH    = 8;  
public static final int FACTOR_FLIP         = 9;  
public static final int FACTOR_ANYTHING     = 10;  
public static final int FACTORZ_COUNT       = 11;    // should be last+1  

private static final int GESTURE_TAP = 0;  
private static final int GESTURE_DRAG = 1;  
private static final int GESTURE_PINCH_OR_ZOOM = 2; 

然后再来看Monkey.java源码中的processOptions()方法

private boolean processOptions() {  
    // quick (throwaway) check for unadorned command  
    if (mArgs.length < 1) {  
        showUsage();  
        return false;  
    }  

    try {  
        String opt;  
        while ((opt = nextOption()) != null) {  
            if (opt.equals("-s")) {  
                mSeed = nextOptionLong("Seed");  
            } else if (opt.equals("-p")) {  
                mValidPackages.add(nextOptionData());  
            } else if (opt.equals("-c")) {  
                mMainCategories.add(nextOptionData());  
            } else if (opt.equals("-v")) {  
                mVerbose += 1;  
            } else if (opt.equals("--ignore-crashes")) {  
                mIgnoreCrashes = true;  
            } else if (opt.equals("--ignore-timeouts")) {  
                mIgnoreTimeouts = true;  
            } else if (opt.equals("--ignore-security-exceptions")) {  
                mIgnoreSecurityExceptions = true;  
            } else if (opt.equals("--monitor-native-crashes")) {  
                mMonitorNativeCrashes = true;  
            } else if (opt.equals("--ignore-native-crashes")) {  
                mIgnoreNativeCrashes = true;  
            } else if (opt.equals("--kill-process-after-error")) {  
                mKillProcessAfterError = true;  
            } else if (opt.equals("--hprof")) {  
                mGenerateHprof = true;  
            } else if (opt.equals("--pct-touch")) {  
                int i = MonkeySourceRandom.FACTOR_TOUCH;  
                mFactors[i] = -nextOptionLong("touch events percentage");  
            } else if (opt.equals("--pct-motion")) {  
                int i = MonkeySourceRandom.FACTOR_MOTION;  
                mFactors[i] = -nextOptionLong("motion events percentage");  
            } else if (opt.equals("--pct-trackball")) {  
                int i = MonkeySourceRandom.FACTOR_TRACKBALL;  
                mFactors[i] = -nextOptionLong("trackball events percentage");  
            } else if (opt.equals("--pct-rotation")) {  
                int i = MonkeySourceRandom.FACTOR_ROTATION;  
                mFactors[i] = -nextOptionLong("screen rotation events percentage");  
            } else if (opt.equals("--pct-syskeys")) {  
                int i = MonkeySourceRandom.FACTOR_SYSOPS;  
                mFactors[i] = -nextOptionLong("system (key) operations percentage");  
            } else if (opt.equals("--pct-nav")) {  
                int i = MonkeySourceRandom.FACTOR_NAV;  
                mFactors[i] = -nextOptionLong("nav events percentage");  
            } else if (opt.equals("--pct-majornav")) {  
                int i = MonkeySourceRandom.FACTOR_MAJORNAV;  
                mFactors[i] = -nextOptionLong("major nav events percentage");  
            } else if (opt.equals("--pct-appswitch")) {  
                int i = MonkeySourceRandom.FACTOR_APPSWITCH;  
                mFactors[i] = -nextOptionLong("app switch events percentage");  
            } else if (opt.equals("--pct-flip")) {  
                int i = MonkeySourceRandom.FACTOR_FLIP;  
                mFactors[i] = -nextOptionLong("keyboard flip percentage");  
            } else if (opt.equals("--pct-anyevent")) {  
                int i = MonkeySourceRandom.FACTOR_ANYTHING;  
                mFactors[i] = -nextOptionLong("any events percentage");  
            } else if (opt.equals("--pct-pinchzoom")) {  
                int i = MonkeySourceRandom.FACTOR_PINCHZOOM;  
                mFactors[i] = -nextOptionLong("pinch zoom events percentage");  
            } else if (opt.equals("--pkg-blacklist-file")) {  
                mPkgBlacklistFile = nextOptionData();  
            } else if (opt.equals("--pkg-whitelist-file")) {  
                mPkgWhitelistFile = nextOptionData();  
            } else if (opt.equals("--throttle")) {  
                mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");  
            } else if (opt.equals("--randomize-throttle")) {  
                mRandomizeThrottle = true;  
            } else if (opt.equals("--wait-dbg")) {  
                // do nothing - it's caught at the very start of run()  
            } else if (opt.equals("--dbg-no-events")) {  
                mSendNoEvents = true;  
            } else if (opt.equals("--port")) {  
                mServerPort = (int) nextOptionLong("Server port to listen on for commands");  
            } else if (opt.equals("--setup")) {  
                mSetupFileName = nextOptionData();  
            } else if (opt.equals("-f")) {  
                mScriptFileNames.add(nextOptionData());  
            } else if (opt.equals("--profile-wait")) {  
                mProfileWaitTime = nextOptionLong("Profile delay" +  
                            " (in milliseconds) to wait between user action");  
            } else if (opt.equals("--device-sleep-time")) {  
                mDeviceSleepTime = nextOptionLong("Device sleep time" +  
                                                  "(in milliseconds)");  
            } else if (opt.equals("--randomize-script")) {  
                mRandomizeScript = true;  
            } else if (opt.equals("--script-log")) {  
                mScriptLog = true;  
            } else if (opt.equals("--bugreport")) {  
                mRequestBugreport = true;  
            } else if (opt.equals("--periodic-bugreport")){  
                mGetPeriodicBugreport = true;  
                mBugreportFrequency = nextOptionLong("Number of iterations");  
            } else if (opt.equals("-h")) {  
                showUsage();  
                return false;  
            } else {  
                System.err.println("** Error: Unknown option: " + opt);  
                showUsage();  
                return false;  
            }  
        }  
    } catch (RuntimeException ex) {  
        System.err.println("** Error: " + ex.toString());  
        showUsage();  
        return false;  
    }  

    // If a server port hasn't been specified, we need to specify  
    // a count  
    if (mServerPort == -1) {  
        String countStr = nextArg();  
        if (countStr == null) {  
            System.err.println("** Error: Count not specified");  
            showUsage();  
            return false;  
        }  

        try {  
            mCount = Integer.parseInt(countStr);  
        } catch (NumberFormatException e) {  
            System.err.println("** Error: Count is not a number");  
            showUsage();  
            return false;  
        }  
    }  

    return true;  
}  

所以,Monkey运行结果中对应的就应该是:

数字 对应量 解释
0 –pct-touch//touch events percentage 触摸事件百分比(触摸事件是一个在屏幕单一位置的按下-抬起事件)
1 –pct-motion//motion events percentage 手势事件百分比(手势事件是由一个在屏幕某处的按下事件、一系列的伪随机移动、一个抬起事件组成)即一个滑动操作,但是是直线的,不能拐弯
2 –pct-pinchzoom//pinch zoom events percentage 二指缩放百分比,即智能机上的放大缩小手势操作
3 –pct-trackball//trackball events percentage 轨迹球事件百分比(轨迹球事件包括一个或多个随机移动,有时还伴有点击。轨迹球现在智能手机上已经没有了,就是类似手柄的方向键一样)
4 –pct-rotation//screen rotation events percentage 屏幕旋转百分比,横屏竖屏
5 –pct-nav//nav events percentage “基本”导航事件百分比(导航事件包括上下左右,如方向输入设备的输入)老手机的上下左右键,智能机上没有
6 –pct-majornav//major nav events percentage “主要”导航事件百分比(这些导航事件通常会引发UI的事件,例如5-way pad的中间键、回退键、菜单键)
7 –pct-syskeys//system(key) operations percentage “系统”按钮事件百分比(这些按钮一般专供系统使用,如Home, Back, Start Call, End Call,音量控制)
8 –pct-appswitch//app switch events percentage 启动activity事件百分比。在随机的间隔里,Monkey会执行一个startActivity()调用,作为最大程度覆盖包中全部Activity的一种方法
9 –pct-flip//keyboard flip percentage 键盘轻弹百分比,如点击输入框,键盘弹起,点击输入框以外区域,键盘收回
10 –pct-anyevent//anyevents percentage 其他类型事件百分比。包括了其他所有的类型事件,如按键、其他不常用的设备上的按钮等等。

至此,monkey运行结果的查看结束。