自动遍历概述、为什么要做自动遍历测试、自动遍历测试现状
maxim 的使用、基本参数、高级用法

自动遍历测试概述


monkey测试

提到自动遍历测试,这就不得不提 google 官方的 monkey 测试,但 monkey 测试的初衷并不是为了自动遍历测试,它是为了解决压力测试 / 兼容性测试的,为了测试每个 activity 响应事件的能力。由于它可生成一些伪随机事件,早期很多公司采用 monkey 测试来做兼容性测试和一定程度的自动遍历测试

monkey测试的来源

Monkey 测试这个名字来源于 “无限猴子定理”:即 “让一只猴子一直在打字机上按键,最终能完成莎士比亚的全部工作。” 猴子测试就是百般刁难,乱按一通,系統也不能宕机或者数据出现差错,这样才能称得是经得起考验的程序。
Monkey 测试是 Android 自动化测试里面使用比较简单的方法,可以很大程度检验我们的程序是否稳定,能否经得起压力测试,可以根据实际情况调整测试的强度和测试重点。这个也是在开发 APP 的时候最常用的方法,基本上每个版本发布之前,大家都会进行比较高强度的 Monkey 测试。
它更多针对的不是功能点,而是业务流程。功能点好测,它是明确的。而业务流程难测,它是随机组合的,一切有可能出现的操作,都是一个测试的流程。而我们用例,大部分是正常的业务流程,而异常测试有不可能把所有的流程测到,所以出现了猴子测试的概念。

自动遍历


为什么要进行自动遍历测试?
自动遍历测试的需求是什么?

业务常见问题

  1. 功能问题
    • app 某特定界面崩溃
    • app 某接口报错或修改导致老版本数据显示异常
    • 某页面详情页特定信息字段内容丢失或数据异常
    • 老功能不可用,如微信分享功能
  2. 兼容性问题
    • 网络慢时发出请求退出该页面,当前页面崩溃
    • 某些界面在 4.4 或 5.5 系统上操作体验不同

测试痛点

  1. 无法用 UI 自动化覆盖所有测试场景
    • 没有采用合理的分层测试体系,错误的将接口层的测试放在 UI 层
    • 没有采用 PageObject 模式,快速迭代的页面变化导致自动化用例维护成本高
    • 对自动化框架掌握程度不够,弹窗处理,框架不稳定等情况出现就无法应对
    • 自动化测试无法很好支撑一些特殊场景如弱网、某本电子小说翻 1000 页,断言不够灵活
  2. 测试内容太多导致手工测试无法充分覆盖
    • 界面需要校验的字段多
    • 接口正确性:后端接口传输数据的变化和内容
    • 专项测试回归难度大:内存泄漏、健壮性测试、弱网等测试过程太多
    • 回归工作量大:不回归又会漏测

测试手段改进

  1. 分层测试
    • 合理划分接口测试与 UI 测试的比例与业务边界
    • 自动化测试覆盖主要业务流程
  2. 手工测试
    • 覆盖新功能测试,探索式测试
  3. 遍历测试手段
    • code less:用例维护成本降到最低
    • automate:尽可能的自动化覆盖回归业务

自动遍历测试的需求

  1. 可控
    • 可定义遍历的路径
  2. 可定制
    • 可实现自动输入,自动滑动等基础行为
  3. 结果可分析
    • 点击前后的截图对比
    • 结果的数据建模
    • 新老版本 / 稳定版本 UI Diff

综上:自动遍历是介于人工测试和自动化测试之间的一种测试手段

自动遍历发展现状


传统的 monkey 测试只能作为稳定性测试 / 兼容性测试工具,且容易卡在某些简单页面,比如登陆页面这种可操作内容很少的页面,又或者经常停留在同一个页面,导致测试效果不佳。但这也给了我们很多的启发,因此就有很多大厂或开发者就尝试对传统的 monkey 测试进行改进,如下(不完全):

  1. 百度的 SmartMonkey
  2. 腾讯的 NewMonkey
  3. 蚂蚁金服 vigossjjj 的 smart_monkey
  4. 阿里 Macaca 的 NoSmoke
  5. 熊猫的 zhangzhao 的 MaximFastmonkey
  6. 霍格沃兹测试学院黄延胜的 AppCrawler帮助文档
  7. 新浪 JustinMa 的 UICrawler
  8. Google 的 App Crawler

Maxim 的使用


基于遍历规则的高性能 Android Monkey,适用于真机 / 模拟器的 APP UI 压力测试

官方文档

maxim github地址

初探 maxim

  1. 下载官方项目 (未开源,提供预编译 jar 包供使用)
  2. push jar 包到手机 (建议 push 到 / sdcard)
adb push framework.jar /sdcard
adb push monkey.jar /sdcard
  1. 执行测试命令
adb shell CLASSPATH=/sdcard/monkey.jar:/sdcard/framework.jar exec app_process /system/bin tv.panda.test.monkey.Monkey -p com.panda.videoliveplatform --uiautomatormix --running-minutes 60 -v -v

参数说明

  1. 兼容 google 官方 monkey 测试参数,如-p <package_name> --throttle 300 -s 300 -v -v -v
  2. 测试策略
    • --uiautomatormix:混合模式(70% 控件解析随机点击,其余 30% 按原 Monkey 事件概率分布),直接使用底层 accessibiltyserver 获取界面接口解析各控件,随机选取一个控件执行 touch 操作
    • --pct-uiautomatormix n:可自定义混合模式中控件解析事件概率
    • --uiautomatordfs: DFS 模式,深度遍历(除 android 5)
    • --uiautomatortroy:Troy 模式,控件选择策略,按max.xpath.selector配置的高低优先级来进行深度遍历
    • --act-whitelist-file /sdcard/awl.strings:定义白名单,awl.strings控制只在几个特定的 activity 里跑
    • --act-blacklist-file /sdcard/abl.strings:定义黑名单,abl.strings控制跳转不会进入到几个特定的 activity 里
    • --running-minutes 60:设置执行时间

高级用法详解

  1. 随机输入max.strings
600519
123456
601857
  1. 黑白名单awl.stringsabl.strings

对跳转做更细粒度控制,比如控制仅在几个特定 Activity 中跑 monkey
adb 命令中需添加参数 --act-whitelist-file /sdcard/awl.strings 定义白名单
--act-blacklist-file /sdcard/abl.string 定义黑名单

com.panda.videoliveplatform.activity.WelcomeActivity
com.panda.videoliveplatform.activity.SplashWakeActivity
com.panda.videoliveplatform.activity.MainFragmentActivity
com.panda.videoliveplatform.activity.LiveRoomActivity
  1. 配置max.config

控制 app 启动和唤醒后等待时间、开启截图和开启随机输入开关,保存 pagesource 开关,崩溃回溯图片区间张数

max.startAfterNSecondsofsleep = 6000
max.wakeupAfterNSecondsofsleep = 4000
max.takeScreenShot  = true
max.flushImagesThreshold  = 20
max.savePageSource  = false
max.saveCurrentEventPoint = false
max.randomPickFromStringList = true
  • takeScreenShot:开启截图
  • flushImagesThreshold:崩溃回溯区间图片张数,且需要在 adb 命令中加入参数--imagepolling
  1. 特殊事件序列max.xpath.actions

如设置登录事件(需提前安装 ADBKeyBoard)

  [
	{
		"prob": 1,
		"activity": "com.android.dazhihui.ui.screen.stock.LoginScreen",
		"actions": [
			{
				"xpath": "//*[@resource-id='com.android.dazhihui:id/switch_login_type']",
				"action": "CLICK",
				"index": 0,
				"throttle": 1000
			},
			{
				"xpath": "//*[@resource-id='com.android.dazhihui:id/loginNick']",
				"action": "INPUTTEXT",
				"text": "monkeyjerry",
				"index": 0,
				"throttle": 2000
			},
			{
				"xpath": "//*[@resource-id='com.android.dazhihui:id/loginPwd']",
				"action": "INPUTTEXT",
				"text": "123456a",
				"index": 0,
				"throttle": 2000
			},
			{
				"xpath": "//*[@resource-id='com.android.dazhihui:id/confirmBtn']",
				"index": 4,
				"action": "CLICK",
				"throttle": 2000
			}
		]
	}
]
  • prob:发生概率
  • activity:可选,指定则仅在设置的activityadb shell dumpsys window w | grep mCurrent) 执行action
  • xpath:控件的xpath,resource-id,class,text,content-desccontains模糊定位(可参考博客 xpath 定位)
  • actionclick,inputtext,touch,swipe,keyevent

特殊事件的 log 如何查看?

  1. Maxim运行开头会打印出loadSpecial Event
  2. 解析生成tree结构如0|2|1|2|5|6,.....表示index|height|depth|childCount|descendantCount,className,contentDesc,text,xpath,clickable,rect,然后针对xpath进行查找,找到则打印Find it
  1. 黑空间和黑区域屏蔽max.widget.black

屏蔽某些界面的某些空间或者区域,如屏蔽广告区域

[
  {
    "activity": "com.panda.videoliveplatform.activity.MainFragmentActivity",
    "xpath": "//*[@class='android.widget.TextView' and @text='我的校园' and @resource-id='com.panda.videoliveplatform:id/tv_title']"
  },
  {
    "activity": "com.panda.videoliveplatform.activity.MainFragmentActivity",
    "xpath": "//*[@class='android.widget.TextView' and @text='车队' and @resource-id='com.panda.videoliveplatform:id/tv_title']",
    "index": 0,
    "bounds": "[0,633][900,789]"
  },
  {
    "activity": "com.panda.videoliveplatform.activity.MainFragmentActivity",
    "bounds": "[0,1107][900,1263]"
  }
]

仅配置bounds屏蔽某个区域,在该区域内的控件或坐标不会被点击
仅配置xpath查找匹配的控件,屏蔽点击该控件
同时配置xpath+bounds则查找匹配的控件,当控件存在时屏蔽指定的区域

  1. Troy模式max.xpath.selector

控制控件优先级
控件选择策略会按 1first 2select 3last 执行并屏蔽 black 来执行遍历

[
{
    "firstList":
    [
        {  "xpath": "//*[contains(@text,'绝地求生')]" }
    ],
    "selectList":
    [
        {  "xpath": "//*[@clickable='true']" },
        {  "xpath": "//*[@clickable='true']//*[contains(name(),'Text')]" },
        {  "xpath": "//*[@clickable='true']//*[contains(name(),'Button')]" },
        {  "xpath": "//*[@clickable='true']//*[contains(name(),'Image')]" }
    ],
    "lastList":
    [
        {  "xpath": "//*[../*[@selected='true']]" },
        {  "xpath": "//*[../../*/*[@selected='true']]" },
        {  "xpath": "//*[../../*/*[@selected='true'] and contains(@resource-id,'tab_')]" },
        {  "xpath": "//*[contains(@resource-id,'HorizontalScrollView')]" }
    ],
    "blackList":
    [
        {  "xpath": "//*[contains(@resource-id,'wrapper_in_custom_title_bar')]//*[contains(@resource-id,'right_button')]" },
        {  "xpath": "//*[contains(@resource-id,'share')]" }
    ]
}
]

我的 maxim 配置

adb push framework.jar /sdcard
adb push monkey.jar /sdcard
adb install ADBKeyBoard.apk
adb shell rm -rf /sdcard/Android/data/.dzh
adb shell pm clear com.android.dazhihui
adb shell rm -rf /sdcard/log_error_dzh.txt
adb push max.xpath.actions /sdcard
adb push max.config /sdcard
adb push max.strings /sdcard
  • mix
adb shell CLASSPATH=/sdcard/monkey.jar:/sdcard/framework.jar exec app_process /system/bin tv.panda.test.monkey.Monkey -p com.android.dazhihui --throttle 500 --imagepolling --uiautomatormix --running-minutes 60 -v -v>d:/monkey.log
  • dfs
adb shell CLASSPATH=/sdcard/monkey.jar:/sdcard/framework.jar exec app_process /system/bin tv.panda.test.monkey.Monkey -p com.android.dazhihui --throttle 500 --imagepolling --uiautomatordfs --running-minutes 60 -v -v>d:/monkey.log

参考


  1. 基于 Android Monkey 二次开发,实现高速点击的 Android Monkey
  2. seveniruby(霍格沃兹测试学院黄延胜)的自动遍历测试分享