Android App压力测试

Posted 胖子爱你520

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android App压力测试相关的知识,希望对你有一定的参考价值。

前言:写这篇文章的目的,一是因为不少同学作为android开发,很少会自己去做压力测试,不了解相关的技术,不知道压力测试是什么、怎么工作的;二是询问过身边的一些测试同学,他们进行压力测试的时候,很多情况只是执行monkey的随机操作,也有部分同学不会写测试脚本,那么本篇文章就应运而生了!

一、背景

1.为什么要进行压力测试?

我们都知道一款产品上线之前都需要进行的一步操作就是测试验收,那么人工手动的测试是带有一定的偶然性的,因为没有人知道哪一步操作会出现问题(例如Crash、ANR等),还有对于一些偶发现象,人为难以复现的场景,开发人员无法针对性的解决问题,那么进行压力测试就是尽可能的模拟各种使用场景,最大程度发现隐藏的问题。

压力测试的目标:

  • 1.提高产品稳定性
    产品的稳定性是App产品所有指标中非常重要的一项,有机构统计70%的用户在使用App时会遇到各种不稳定的问题,当出现异常问题时,有的用户会忍受继续使用,有的用户可能就会放弃使用直接卸载App了,这取决于应用的使用场景和发展阶段,例如微信、QQ、支付宝等等,人们已经离不开这些和生活息息相关的产品了,哪怕偶尔出现一两次不稳定的问题,用户很大程度上也不会卸载,但是如果这发生在一款初创期的App上,用户可能直接就卸载了,可能给公司带来致命的打击。

  • 2.提高产品留存率
    机构统计有5%的用户在遇到页面延时响应时间过长的时候,会选择放弃继续使用或者卸载App,当用户对这样的一款App产生反感情绪时,产品的DAU和留存率就会降低。所以提高产品的稳定性、提高产品的留存率,为公司带来更大的收益,压力测试势在必行!

2.什么时候进行压力测试?

这个没有明确的要求,但是一般会选择在首轮功能测试验收通过后进行,这样能最大程度避免因为功能性bug导致的压力测试结果异常。因为压力测试是自动化执行,所以当我们下班的时候,就可以执行测试脚本,第二天早上上班的时候来查看压力测试结果,测试工作互不耽误。

二、理论

1.手工测试场景


当我们手工模拟一个搜索操作时,会按照以上的步骤来:1.点击搜索框(弹起软键盘),2.输入关键词,3.点击搜索按钮,搜索结果就会出现在下面的网页中。

2.自动测试场景

将我们上述的手工操作,转换成机器语言,应该是什么样呢?

点击输入框 -> 输入关键词(键盘事件)-> 点击搜索 -> 选择想要查看的结果条目 -> 滚动浏览,这就是机器自动化的流程,那么我们应该做的就是模拟手工操作的事件流。

3.Monkey工具

  • 1.什么是Monkey?
    Monkey是Android SDK自带的测试工具,是一个命令行工具,可以运行在模拟器里或实际设备中。可以运行在模拟器中或者实际设备中,它向系统发送伪随机的用户事件流(如按键输入,触摸屏输入,手势输入等),实现对正在开发的应用程序进行压力测试。由于测试事件和数据都是随机的,不能自定义,所以有很大的局限性。
  • 2.Monkey在哪儿?
    存在于我们的Android手机中,需要借助ADB来和Monkey进行通信。

4.ADB命令

  • 1.什么是ADB?
    Android 调试桥 (adb) 是一种功能多样的命令行工具,可让您与设备进行通信。adb 命令便于执行各种设备操作(例如安装和调试应用),并提供对 Unix shell(可用来在设备上运行各种命令)的访问权限。
  • 2.ADB在哪儿?
    adb 包含在 Android SDK 平台工具软件包中。您可以使用 SDK 管理器下载此软件包,管理器会将此软件包安装在 android_sdk/platform-tools/。或者,如果您需要独立的 Android SDK 平台工具软件包,可以点击此处进行下载

5.什么是MonkeyScript

MonkeyScript是官方提供的,除了像猴子一样随机乱点之外,还可以通过编写脚本的形式,完成一系列固定的操作。MonkeyScript提供一整套完善的API来进行支持,主要还是基于坐标点的操作,包含常用的:点击、长按、输入、等待等操作。

6.什么是MonkeyRunner

monkey和monkeyrunner都是android sdk提供的测试命令,但monkeyrunner和money没有什么直接的关系,monkey是在设备直接运行adb shell命令生成随机事件来进行测试的。相比较而言,monkeyrunner则是通过API发送特定的命令和事件通过工作站来控制设备。

7.MonkeyRunner API

MonkeyRunner工具主要有三个类:MonkeyRunner、MonkeyDevice、MonkeyImage。

  • 1.MonkeyRunner类:提供连接真机和模拟器方法waitForConnection(float timeout,string deviceid),还有显示提示显示信息的alert()方法。
  • 2.MonkeyDevice类:提供了安装和卸载程序包、开启Activity、发送按键和点击事件、运行测试包等方法。
  • 3.MonkeyImage类:在测试过程中用来保存测试截图,保存各种格式,并可以比较两个MonkeyImage对象。

三、实战

1.App压力测试实战

  • 1.在手机开发者选项中,将USB调试勾选。
  • 2.确认手机和电脑已经成功连接。

    在连接设备列表中能发现自己的设备说明连接成功。
  • 3.安装测试App
  • 4.发送压力测试命令(随机)
adb shell monkey 100

这时手机就会模拟各种随机操作100次。

执行完后在命令行会看到以下内容:

可以看到我们设置的是执行100次操作,这里Events injected也收到了100次事件,说明过程中没有出现异常,否则Events injected数量会少于100,整个操作过程耗时5613ms。

  • 5.获取App包名
    以上我们执行的是随机命令,如果我们想针对某一个App进行压力测试怎么办呢?首先我们需要获取想要测试的App的包名,可以借助以下一行命令:
adb logcat | grep START

这里拿计算器应用为例,执行后,然后点击手机中的计算器应用,就会发现在最后一行会输出计算器的相关信息:

其中cmp=com.meizu.flyme.calculator就是计算器的包名了。

  • 6.给指定App进行压力测试
adb shell monkey -p com.meizu.flyme.calculator 1000

这行代码会对我们的计算器应用执行1000次的随机操作。

2.Monkey高级参数的使用

  • 1.throttle参数
    指定事件之间的间隔(毫秒)
adb shell monkey --throttle <milliseconds>
  • 2.seed参数
    指定随机生成数的seed值
adb shell monkey -s <seed> <event-count>

我们利用monkey进行压力测试的时候,monkey生成的事件流都是随机的,如果在执行过程中发生异常的时候,测试同学可能找开发去修改,开发同学要求测试同学复现操作步骤,那么这个时候就很难复现了。如果我们能够指定seed值的话,那么monkey就会执行相同的操作序列,就能很方便的复现之前出现的异常问题。

  • 3.触摸事件
    设定触摸事件百分比
adb shell monkey --pct-touch <percent>

同样以计算器为例,在命令中增加一个-v参数,能够看出操作执行过程中的百分比:

Event 0代表的是touch事件,因为我们指定了–pct-touch 100,所以其他的触摸事件都是0,touch事件是100,另外下面的结果中出现了多次ACTION_DOWN和ACTION_UP,按下和弹起都是配对出现的,这就是模拟一次点击过程。

如果我们不指定–pct-touch 100,来看看执行效果:

可以看出事件很随机的分布,其中touch事件占了15%,从下面打出的日志可以看出,还有像Traceball和rotation这些事件的触发。

  • 4.动作事件
    设定动作事件百分比
adb shell monkey --pct-motion <percent>

这里需要注意一点是动作事件和其他事件百分比的和要等于100,如果不等于100的话,则会将剩余部分随机操作。

这里我们指定了touch事件50%,motion事件30%,所以剩下的8种事件类型占比是随机分配的。

  • 5.轨迹球事件
    设定轨迹球事件百分比
adb shell monkey --pct-traceball <percent>
  • 6.基本导航事件
    设定基本导航事件百分比,输入设备的上、下、左、右
adb shell monkey --pct-nav <percent>
  • 7.主要导航事件
    设定主要导航事件百分比,兼容中间键、返回键、菜单键
adb shell monkey --pct-majornav <percent>
  • 8.系统导航事件
    设定系统导航事件百分比,HOME、BACK、拨号和音量键
adb shell monkey --pct-syskeys <percent>
  • 9.启动Activity事件
    设定启动Activity事件百分比,会在多个app之间进行切换
adb shell monkey --pct-appswitch <percent>
  • 10.不常用事件
    设定不常用事件百分比
adb shell monkey --pct-anyevent <percent>
  • 11.崩溃事件
    忽略崩溃和异常

在monkey执行过程中,如果遇到一次crash,则进程会中断,不会继续往下进行,那么我们如果希望monkey能够执行完所有的事件的话,就需要用到这个参数。

adb shell monkey --ignore-crashes <event-count>
  • 12.超时事件
    忽略超时事件

和崩溃事件一样,如果monkey遇到ANR时,也会中断,所以我们需要忽略超时事件的话,可以用到这个参数。

adb shell monkey --ignore-timeouts <event-count>

3.Crash结果分析

  • 1.安装一个会发生Crash的app
  • 2.执行monkey进行压力测试
  • 3.分析Crash的Exception信息


可以看到当monkey触发了某一个crash事件时,进程停止了,同时会打印出crash的相关信息,这是Event injected事件是533,我们命令中指定的是1000,所以当异常发生时,不会继续执行剩下的操作。

注意到在log的最后有一行:

System appears to have crashed at event 533 of 1000 using seed 1574782034909

前面我们说过,如果我们想要重复上一次的过程的话,需要指定一个seed值去模拟上一次的操作,这里可以使用seed 1574782034909,这样执行过程就会和上次一模一样,这样就可以复现我们刚才崩溃的场景。

如果我们希望出现crash时,monkey仍然执行完1000次操作时,这时就可以用到上面介绍到的–ignore-crashes。

可以看到,我们指定了seed,模拟上一次的操作流,同时也使用了–ignore-crashes,crash发生了,但是仍会执行完,所以Event injected为1000,但是这个时候想看到seed值会发现没有了,因为当crash发生时,仍然要求monkey继续执行,所以就会随机生成一个新的seed值。

4.ANR结果分析

  • 1.安装一个会发生ANR的app
  • 2.执行monkey进行压力测试
  • 3.分析ANR的Exception信息

日志非常多,截取部分日志来看看:


可以看到当执行到第84个操作时,就发生了ANR,发生的原因在日志中也有具体的描述,如果我们希望复现这个ANR的话,可以依靠此次产生的seed。同理如果希望事件继续执行完,可以借助–ignore-timeouts命令。

5.MonkeyScript实战

在monkey帮我们完成稳定性测试之后,我们如果有一种需求,模拟重复操作事件流100次,而monkey是随机操作,那么这时候就要使用MonkeyScript了。

adb shell monkey -f <script-file> <event-count>

在编写MonkeyScript之前,来先了解一些常用命令:
1.DispatchTrackball命令
轨迹球事件

DispatchTrackball(long downtime,long eventide,int action,float x,float y,
float pressure,float size,int metastate,float xprecision,float yprecision,
int device,int edgeflags)

long downtime指键最初被按下的时间
long eventtide指事件发生的时间
int action指具体操作的动作,如按下
float x,float y指x和y的坐标
float pressure压力事件的大小(0~1)
float size指触摸的记事值(0~1)
int metastate指当前按下mate键的标识
float xprecision,float yprecision指x和y坐标的精确值
int device事件的来源(0~x)
int edgeflags指超出屏幕了范围

我们这里用三个参数就可以了,分别是:
int action(0表示按下,1表示弹起)
如果我们想要模拟点击事件的话,需要传输两个参数,一个命令传输0,表示按下,另一个传输1,表示弹起,这样我们可以实现点击的过程。

在点击的过程中,我们需要确定点击的点,这里就是指需要确定点击的范围,即就是x和y的坐标:float xfloat y

2.DispatchPointer命令
点击事件

DispatchPointer(long downtime,long eventtide,int action,float x,float y,
float pressure,float size,int metastate,float xprecision,float yprecision,
int device,int edgeflags)

参数含义和DispatchTrackball一样。

3.DispatchString命令
输入字符串命令

DispatchString(String text)

接收一个要输入的字符串。

4.LaunchActivity命令
启动应用

LaunchActivity(package,Activity)

#package指App包名
#Activity指被启动页面的名称

5.UserWait命令
等待事件

UserWait(1000)

等待的时间,单位为毫秒

6.DispatchPress命令
按下键值

DispatchPress(int keycode)

keycode 66 回车键
根据自己的需要传入具体对应的键值即可

MonkeyScript实战部分
在了解完上述的几个命令之后,我们正式开始编写脚本。这里我们模拟一个用户在浏览器搜索100次的场景。用户每次的操作流程如下:

那我们将这几个步骤转化为MonkeyScript支持的语言。

首先MonkeyScript有一些固定的脚本头,需要写在脚本的最前面。

type=user
count = 1
speed = 1.0
start data >>

然后才是我们要执行的步骤脚本,完整的脚本如下:

type=user
count = 1
speed = 1.0
start data >>

LaunchActivity(com.zhangyan.monkeydemo,com.zhangyan.monkeydemo.MainActivity)
UserWait(2000)
DispatchPointer(10,10,0,200,200,1,1,-1,1,1,0,0)
DispatchPointer(10,10,1,200,200,1,1,-1,1,1,0,0)
DispatchString(China)
UserWait(1000)
DispatchPress(66)
UserWait(1000)
DispatchPointer(10,10,0,1350,250,1,1,-1,1,1,0,0)
DispatchPointer(10,10,1,1350,250,1,1,-1,1,1,0,0)
UserWait(6000)
DispatchPointer(10,10,0,1200,250,1,1,-1,1,1,0,0)
DispatchPointer(10,10,1,1200,250,1,1,-1,1,1,0,0)
UserWait(3000)

解释一下参数:

DispatchPointer(10,10,0,200,200,1,1,-1,1,1,0,0)
DispatchPointer(10,10,1,200,200,1,1,-1,1,1,0,0)

这是一个模拟点击事件,所以按下和弹起是成对出现的,第三个参数action分别是0和1表示down和up,第四位和第五位参数是点击的x和y坐标点,那么这个点如何获取呢?需要借助Android SDK中的一个uiautomatorviewer,它的路径在我的/Library/Android/sdk/tools/bin/下,你们可以在你们对应的安装目录下找到这个命令。运行起来后如下所示:

我们可以借助这个获取我们想要的x和y值,放到参数中就行了。

脚本写完了保存一下,此时执行adb shell monkey -f monkey.script 2是无效的,为什么呢?
因为monkey是存在我们的手机设备中的,而我的monkey.script脚本存在我的电脑中的,所以手机中的monkey是无法直接获取到电脑设备的脚本的,所以我们需要将monkey.script放到手机中去,执行以下这段命令:

adb push monkey.script /data/local/tmp/

然后执行这条命令,注意路径得写文件的绝对路径,也就是我们刚才设置的路径。

adb shell monkey -f /data/local/tmp/monkey.script 2


可以看到脚本执行起来后,app就会按照我们设定的操作执行,重复的次数就是我们设置的2次,当操作全部执行完成后,终端会有如下信息:

那么这里为什么Event injected等于30呢?其实这个操作的个数就是我们脚本里的命令数,执行多少次就是它的乘积。

6.MonkeyRunner实战

MonkeyScript虽然很强大,能够帮助我们完成丰富的操作,但是它也有很多限制之处,比如我们在自动化过程中想要完成截屏操作,那它就无法完成了,此时就需要用到MonkeyRunner了。

在使用MonkeyRunner之前,来先了解一些常用命令。

MonkeyRunner API 主要通过下面三个包:
MonkeyRunner: 主要提供了MonkeyRunner应用的辅助方法以及用来连接设备或是模拟器的方法,并提供UI支持等。
MonkeyDevice: 代表一个设备或是模拟器,提供安装、卸载应用的方法,启动一个 Activity,发送按键或是Touch事件等。
MonkeyImage: 代表一个截屏图像,可以截取不同格式的图像,比较两个MonkeyImage图像,保存图像等。

1.MonkeyRunner API - alert
警告框

void alert(String message,String title,String okTitle)

下面来写一段python脚本:

#!/usr/bin/python
#-*- UTF-8 -*-
from com.android.monkeyrunner import MonkeyRunner
MonkeyRunner.alert('Hello World','MonkeyRunner','OK')

执行命令monkeyrunner demo.py

2.MonkeyRunner API - waitForConnection
等待设备连接,有多个device id,需要指明具体哪个设备

waitForConnection(float timeout,String deviceId)//timeout单位为秒

3.MonkeyRunner API - drag
拖动

drag(tuple start,tuple end,float duration,integer steps)

start:启动位置
end:终点位置
duration:手势持续的时间
插值点的步数,默认是10

4.MonkeyRunner API - press
按键

press(String keycode,dictionary type)

keycode:按键的code
type:按键类型:DOWN、UP、DOWN_AND_UP

5.MonkeyDevice API - startActivity
启动应用

startActivity(package+"/"+activity)

6.MonkeyDevice API - touch
点击事件

touch(integer x,integer y,integer type)

7.MonkeyDevice API - type
输入事件

type(String message)

8.MonkeyDevice API - takeSnapshot
截屏事件

MonkeyImage takeSnapshot()

9.MonkeyImage API - sameAs
图像对比

boolean sameAs(MonkeyImage other,float percent)

10.MonkeyImage API - writeToFile
保存图像文件

void writeToFile(String path,String format)

MonkeyRunner实战部分
我们仍然模拟上面的操作,用MonkeyRunner脚本来实现一遍,完整代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice,MonkeyImage
#连接设备
device = MonkeyRunner.waitForConnection(3,"793QADSKAR5G4")
#启动app
device.startActivity("com.zhangyan.monkeydemo/com.zhangyan.monkeydemo.MainActivity")
MonkeyRunner.sleep(2)
#点击搜索框
device.touch(200,200,"DOWN_AND_UP")
MonkeyRunner.sleep(1)
#输入关键词
device.type("China")
MonkeyRunner.sleep(1)
#点击回车键
device.press("KEYCODE_ENTER","DOWN_AND_UP")
MonkeyRunner.sleep(1)
#点击搜索
device.touch(1350,250,"DOWN_AND_UP")
MonkeyRunner.sleep(3)
#截图
image = device.takeSnapshot()
image.writeToFile('./takeSnapshot.png','png')
#点击清除按钮
device.touch(1200,250,"DOWN_AND_UP")
MonkeyRunner.sleep(3)

我的python脚本放在桌面上,所以进入到桌面目录,执行以下命令:

monkeyrunner monkeyrunner.py 

等待几秒设备连接成功,就会看到手机出现和MonkeyScript一样的操作,不过我们的python脚本中有一个截屏的操作,等待脚本执行完,会看到桌面上有一张我们命名好的图片,打开看看图片的内容:

可以看到在我们点击搜索按钮后,就执行了截屏命令,保存到指定路径。

到这里MonkeyRunner的基本用法就讲完了,我们可以看到MonkeyRunner帮助我们完成了整个自动化的过程,如果想要MonkeyRunner执行多次的话,就需要借助python脚本做这样的事情,这样可以完成重复的操作过程。

四、结束语

这篇文章简单的介绍了Monkey相关的知识,包括基础的MonkeyScript使用、MonkeyRunner的使用,大家可以借助这些命令实现自己的一个自动化压力测试的功能,感谢!

以上是关于Android App压力测试的主要内容,如果未能解决你的问题,请参考以下文章

Android App 压力测试 monkeyrunner

Android App 专项测试--压力测试篇

Android App压力测试

Android APP压力测试之理论

Loadrunner实现Android / IOS 手机压力测试(实例)

Android App压力测试之Monkey