1、前情回顾
在此之前,分别通过两篇文章介绍了自动遍历的测试需求、工具选择和AppCrawler的环境安装、启动及配置文件字段基本含义,具体可参考博客:
初探自动遍历测试工具-AppCrawler
以AppCrawler的配置文件完成定制化的自动遍历测试(基础)-01
这里将以实际案例更加细致的说明配置文件的用法和一些特殊场景的处理
2、实操演示
2.1 常规使用
下面我们继续之前的例子,在雪球搜索框输入搜索内容后的页面开始:
-
testcase:设置测试用例,输入alibaba后,点选"阿里巴巴"
yaml写法如下:testcase: name: "XueQiuTestDemo AppCrawler" steps: - { xpath: "//*[contains(@resource-id,'image_cancel')]", action: click } - xpath: home_search action: click - xpath: search_input_text action: alibaba - { xpath: 阿里巴巴, action: click }
-
selectedList:遍历范围设定
接上一步点选"阿里巴巴"后到达如下界面:
我们先看demo配置文件中的原始写法,如下:selectedList: - given: [] when: null then: [] xpath: "//*[contains(name(), 'Button')]" action: null actions: [] times: 0 - given: [] when: null then: [] xpath: "//*[contains(name(), 'Text') and @clickable='true' and string-length(@text)<10]" action: null actions: [] times: 0 - given: [] when: null then: [] xpath: "//*[@clickable='true']/*[contains(name(), 'Text') and string-length(@text)<10]" action: null actions: [] times: 0 - given: [] when: null then: [] xpath: "//*[contains(name(), 'Image') and @clickable='true']" action: null actions: [] times: 0 - given: [] when: null then: [] xpath: "//*[@clickable='true']/*[contains(name(), 'Image')]" action: null actions: [] times: 0 - given: [] when: null then: [] xpath: "//*[contains(name(), 'Image') and @name!='']" action: null actions: [] times: 0 - given: [] when: null then: [] xpath: "//*[contains(name(), 'Text') and @name!='' and string-length(@label)<10]" action: null actions: [] times: 0
原始文件中将所有可点击的控件类型都包括了进去,再加上了部分text长度的限制
现在我们按照自己平常的简便写法重新编写,先设置所有clickable等于true的控件进行点击:在Android中,一个控件如果绑定了用户响应行为的事件,那么clickable的属性值就位true
selectedList: - { xpath: "//*[@clickable='true']", action: click }
-
blackList:黑名单,将不想要被点击的元素加入黑名单中
配置文件原始写法如下,表示将带有2位数字的排除在外,可能是App中包含了很对关于股价展示的,不需要挨个点击:blackList: - given: [] when: null then: [] xpath: ".*[0-9]{2}.*" action: null actions: [] times: 0
我们现在希望不要点击到叉号❌和取消按钮,否则会跳出此页面,那么就可以把其加入黑名单中,如下:
blackList: - xpath: ".*[0-9]{2}.*" - xpath: //*[@resource-id='action_delete_text'] - xpath: //*[@resource-id='action_close']
-
firstList: 优先被遍历
这里我们设置让text包含"股票"的优先遍历firstList: - { xpath: "//*[contains(@text,'股票')]", action: click }
-
lastList:最后被点击
在页面中有很多标签页(例如综合、股票、用户、组合):
每个标签页下面对应着很多控件需要被操作,可是在当前页面下的控件未被遍历完的时候就有可能会点击到其他标签页中了,我们希望的是在一个标签页下完全遍历结束后最后再点击标签控件,这个就可以借助lastList
来完成,让元素在点进标签页后的内容为最后遍历lastList: - { xpath: "//*[contains(@resource-id,'ti_tab_indicator')]//*", action: click }
-
backButton: 当所有元素都被点击后默认后退控件定位
AppCrawler是不知道后退按钮是哪一个的,这个可能会造成的一种情况是,当我们进入一个页面时,还没有对这个页面完全遍历就点到了后退按钮,这样就会造成测试不充分
因此我们可以给它设置一个默认的后退按钮,使所有事件完成后再backbackButton: - { xpath: "//*[contains(@resource-id,'action_back')]", action: click }
这里还要说明一点,如果
backButton
没有配置,当遍历第一个页面的时候被点击了,再下一个页面的时候,因为已经被遍历过了,所以就不会优先去遍历这个回退按钮了,对后面的遍历测试没有大的影响,可能就是第一个页面会有遍历不完全的概率。 -
maxDepth: 遍历的最大深度
有时候我们的页面层次可能很深,每次遍历测试的需求可能不同,有时候可能需要在短时间内测试主要常用界面的功能,有时候可能需要全面的测试,所以测试的深度就不相同,我们可以依靠maxDepth
来进行需求定制,这里以遍历2层深度为例:maxDepth: 2
2.2 特殊技巧
-
findBy:定位方式的选择
findBy
可以设置定位方式,有default
、android
、id
、xpth
方式可选,默认状态会自动判断是否是要android
定位或者ios
定位。当我们的定位很精准的时候,用默认的default
速度会快一点;若是定位符写的不是很精准,在切换到Android定位的时候可能找不到,这个时候就可以尝试将其设置为xpath
方式定位findBy: "xpath"
-
defineUrl = List[String]():用来确定url的元素定位xpath,他的
text
会被取出当做url因素;就是说如果想要当前的页面布局与某个控件之间有层级关系,给定一个标记控件,以此来区分不同的界面(语言的描述怎么样都有点晦涩,还是结合下面的示例来理解吧。。。)
有时候我们会遇见这种情况:设置了clickable
未true
的控件都被遍历,可是运行时发现很多控件都没有被遍历到,一般这种情况有一下两种原因:- 元素属性 clickable本身就为false或者它的父节点等都为false,这样自然是无法遍历到的
- 还有一种情况是同属性的控件在两个
tag
页面都存在,在其中一个tag
页遍历一遍之后,再到下一个tag
页中就会默认已经遍历,不会再进行遍历,如下这种:
在“股票”和“用户”tag
页中,“加自选”和“关注”控件的clickable
及id
属性一样,
他们所属的页面属性也一样,所以会被看做是同一个页面下的同一个控件:
如上这种情况肯定不是我们想要的,我们想要它在股票和用户页都分别进行遍历,更好的覆盖测试,那么就要借助于defineUrl
了;
1)按照上面的介绍,我们首先要找一个标志控件,用来做页面的区分,那么我们首先想到的就是从“股票”和“用户”这两个tag标签属性上来找,遗憾的是最终发现这两个控件的属性全都一毛一样:
2)接着我们就必须从tag页内部来找标志控件了,我们发现在“股票”和“用户”页中搜索出来的结果名称的id是不同的:
3)上面介绍过了defineUrl
是取的text属性值作为标志区分,所以这里取股票页的第一个元素“阿里巴巴”和用户页的第二个元素“阿里巴巴四十大盗”,具体yaml写法如下:
defineUrl: - (//*[contains(@resource-id, "stockName")])[1] - (//*[contains(@resource-id, "user_name")])[2]
缺点: 上面的做法虽然解决了页面区分的问题,但是有一个缺点就是我们定义了遍历的深度,然而使用
defineUrl
之后将每个标志符在的页面都视为一个新的activity,因此遍历深度就会从这里开始重新计算
4)继续解决上述的缺点,我们可以在clickable
之前指定所属的页面,当判断不在此页面后就会自动跳回selectedList: - { xpath: "//*[@resource-id='com.xueqiu.android:id/ll_search_result']//*[@clickable='true']//*", action: click }
5)另外我们之前在
selectList
中写了clickable=true
,而clickable=true
通常只是布局元素,布局元素一般是没有任何属性的,不知道控件里包含什么,这样在截图和生成报告的时候就会造成不精准,截图中的步骤框就很可能选择错误,对我们定位分析问题造成困扰;
所以我们要继续往下找标志符,以Text作为定位标志符:selectedList: - { xpath: "//*[@resource-id='com.xueqiu.android:id/ll_search_result']//*[@clickable='true']//*[contains(@class,'Text')]", action: click }
用Text作为标志符以后所有的Text属性都会遍历一遍,还可以进一步优化,使用id非空作为判定条件,并且通常研发将控件设置id的话很可能此控件有关键的作用
selectedList: - { xpath: "//*[@resource-id='com.xueqiu.android:id/ll_search_result']//*[@clickable='true']//*[@resource-id!='']", action: click }
6)按照上面的写法又引发了新的问题,就是id不为空的时候,我们的tag控件无法被选中了,因为tag控件的id正好为空:
因此我们又需要对selectedList
进行修改,单独增加一条判定条件用来过滤出tag控件;我们注意到它们同属一片有id的区域,并且各自自身有text
:
修改后的selectedList
如下:selectedList: - { xpath: "//*[@clickable='true']//*[contains(@class,'Text')]", action: click } - { xpath: "//*[contains(@resource-id, 'ti_tab_indicator')]//*[contains(@class,'Text')]",action: click }
-
tagLimitMax:最大的点击次数
有时候页面中可能会有多个相同类型的控件,这些控件之间可能只是展示的信息不同,其他功能属性都一直,那么为了保证测试效率可以只设置让它被点击少数次或者一次,通过tagLimitMax
设置即可。tagLimitMax: 1
缺点:这个设置是一个全局的,一旦设置,那么所有的同类型的控件都只会被点击一次,但是像上个例子中的4个tag标签控件虽然是同类型的,但是每一个都需要被点击一次,这样显然就不符合我们的需求了,这个时候就需要
tagLimit
参数了 -
tagLimit:自定义控件类型的点击次数
tagLimit: - xpath: //*[contains(@resource-id, 'ti_tab_indicator')]//*[contains(@class,'Text')] action: click times: 4
-
triggerActions:触发器,特定条件触发执行动作的设置
这个参数是一个非常有用的参数,比如我们可能会遇到如下的情况- 广告、升级弹框在测试过程中突然出现
- 某些动作需要输入
- 某些动作需要特定次数的操作
而这个时候就轮到
triggerActions
大显身手了,它可以对特定的控件指定特定的动作和次数,在每一次遍历执行前都会先执行一遍triggerActions
,它不像testcase是个流形式的,执行完就结束了,triggerActions
会一直伴随着执行- 这样每次出现弹框都会被处理
- 测试中途碰到了账号密码输入框需要输入的可以提前在
triggerActions
中设置
不知道大家还记得在刚开始运行的时候,因为在已进入主页的时候就会有升级弹框出来,所以那时候我在testcase中加入了步骤将其点掉,但是如果在其他页面再次弹出的话就无法处理,现在以此为例,将其加入
triggerActions
triggerActions: - xpath: //*[contains(@resource-id,'image_cancel')] action: click times: 1
3、常见问题补充
-
app运行比较慢,容易超时怎么办?
答:AppCrawler默认每次操作时会等待500ms;通过triggeraction
来解决需要等待的条件,xpath为进度条,action为sleep 1s -
tagLimit会限制同属性但不同层级的元素吗?
答:tagLimit限制的是相同的父节点层级,不管属性,是看布局的层级 -
如何防止遍历的时候不小心跳到别的应用?跳到别的应用后怎么回来?
答:会自动跳转回来的。除非设置了App的白名单 -
页面需要在当前页不停滑动加载测试
答:遍历完当前页后用afterpage
参数设置滑动 -
firstList和lastList可以写多个表达式吗?他们是如何执行的?
答:顺序是这样排列的firstList[0] firstList[1] 排除lastList firstList之后剩下的元素 lastList[0] lastList[1] backbutton
-
appclawer ==>maxDepth:这个层级是如何定义的?
答:maxDepth可以从log中看到,AppCrawler.log中有一个Stack的输出,里面默认保存的是所有activity的栈记录。Main
Main->UserProfile
Main->UserProfile->Login
Main
maxDepth是判断这个堆栈最长的长度,一旦超过就回退