关于UI自动化的抱怨html
听过很多人这样讲 “UI自动化很是不稳定,需求一改,界面一遍,所有都费了”。我相信作过的人可能也会有同感。既然这个问题一直都是存在的,那么为何没有人仔细分析缘由呢?框架
个人老板george曾举了这样一个例子:每当需求变化的时候,开发没有跳起来,反而是测试跳了起来。而后不断的抱怨,界面元素全都改了,个人自动化的用例所有都要废弃掉了。那么咱们是否想过,为何开发能够从容不破的应对产品不断变化的需求?而咱们却不能呢?异步
业内很多人也都放弃了UI自动化,以为接口测试才是最有意义的,的确接口测试相对于UI自动化的来讲,确实稳定的多,可是难道咱们测完接口后,就不须要再测试UI了吗?UI层但是最贴近用户,也就是直接给用户最直接的一层,咱们真的要放弃吗?换句话说,若是客户端开发在分层作的特别差的时候,客户端的接口测试真的会那么好作吗?按下一个home键后再回来,onStop、onResum等一些生命周期里的函数恐怕都很难应付的过来吧。说了这么多,仍是回到正题吧,讲一讲如何写好咱们的UI自动化测试工程。(注意,这里是工程,不是脚本,脚本是很随意的,随便写两句用来测试的,工程就表明是一个能够交付给用户使用的,咱们能够认为开发的代码是咱们要交付给用户的产品,固然咱们的测试代码一样也是要交付给用户的产品,因此后面统一叫测试工程)函数
简陋的测试代码工具
首先来看看以前咱们的UI自动化的代码是怎么写的:布局
这里我使用了Espresso+Uiautomator2框架来进行举例,忽略语法,只说结构的话是否是看上去很熟悉,你们在起初写测试代码的时候应该也是这样的一个方式吧:在测试函数中完成全部的操做实现,若是是通用的操做,可能去封装一个函数,固然有的时候还会用到setup进行初始化,使用teardown来进行收尾。我想说若是是这样的写法的话,咱们的确很难应对变化的需求和界面了,恐怕测试框架的更新换代,都能让咱们的测试代码所有推翻重写。其实综合起来咱们以前的代码存在这样的一些问题:post
一、用例层包含了太多和源码相关的内容,只要源码改动,用例内容必改。测试
二、用例层直接调用原框架内容进行实现,没法处理一些通用的异常状况,也不能适应工具框架的变化。优化
三、通用的一些方法(例如登陆),多个用例集(**Test类)中的用例(test**()函数)可能都须要复用,须要处理好调用的方式。.net
四、启动Activity这样的操做没必要要每一个用例集都写,能够优化一下,抽象出基类。
所以针对这些地方,咱们须要进行优化,咱们接着往下看。
关键字驱动
yoyo(GT的开发)建议我使用这样的一套用例编写的方案,就是基于关键字驱动的方案,在用例层的实现剥离和源代码相关的部分,使用高度抽象。这样的话只要业务的流程不变,界面任意怎么修改,咱们都不须要去修改用例层的代码。整个框架的实现结构以下:
这里若是你们看类图有些吃力的话,能够先看看UML类图介绍。这里先大体说下这些类模块的做用:
BaseTest:全部Test类的基类,也就是测试用例的基类,里面实现ActivityTestRule来启动Activity,若是有须要的状况下能够实现BeforeClass和AfterClass,这两个在整个命令的运行周期内只在开始和结束的地方执行一次。
用例Test:具体的测试用例的实现类,这个能够理解为一个测试集,每一个类中有若干test函数,每一个函数就表明一个测试用例,用例的写法采用关键字驱动的方法。
Key:用枚举定义着全部的关键字。
Command:接口类,供Word实现execute(Obj)的方法。
FrameCommand:基类,供Word层来继承,里面只封装了一个execute(Key, Obj...),主要用在AW中调用KW的实现;这里须要注意和Command接口中execute的区别。
Word层:即图中Login、Enter**Page等,须要实现Command接口中的execute函数,同时继承自FrameCommand,解释为何是Word层,这里须要把这些实现抽象成ActionWord(简称AW)、KeyWord(简称KW),而这二者的区别就是,KeyWord中实现可复用的一些场景,ActionWord中能够包含KeyWord,实现一些不多被复用的场景。
TestContext:将Key中关键字和具体的Word实现的函数进行关联,构造一个map,使得直接经过execute关键字就能调用起对应的函数。
具体实现的代码我先不详细解释,咱们先来看看使用这套框架后,以前实现一样功能代码写成了什么样子:
能够看到,测试用例(这里认为一个test***函数就是一个测试用例)这一层咱们作了高度的抽象,在testPublish这个函数中没有任何与开发源代码或者是资源id有关的信息了,这里的Key.EnterPublishPage就是咱们的关键字,具体的实如今EnterPublishPage这个AW的函数中,这样写用例,当咱们的界面发生了大的改变,例如咱们版本迭代中从发布的两个页面,以下所示:
在新版本改变后,改为了一个界面,以下图:
能够看到界面元素的调整仍是很多的,若是以前咱们可能就得废弃以前的用例从新写了,可是使用了关键字驱动后,咱们的用例层的改变根本不须要作任何的修改,而对应的若是控件ID改变后,咱们只须要修改Word层便可。
这里说下AW和KW之间如何封装,就代码层面,对于编译器来讲,是没有AW和KW之分的,咱们抽象这两层的意思就是,当有一个Word能够被多个用例复用的话,这样咱们就把它封装起来供其余的Word使用。具体还须要在实践过程当中慢慢的体会。
封装测试框架
说完了关键字驱动,须要说下封装框架这一块。看过我以前文章的朋友们应该知道我为何要选择Espresso和Uiautomator,目前谷歌推荐使用这两款框架,有兴趣能够看看谷歌的这两篇原文:
那么两个框架同时加入到咱们的测试工程应该如何去整合代码结构内,这里我本身使用这样的结构,以为还不错的能够参考一下,首先先看下类图:
看完类图后可能有些人已经看出来啦,没错这里使用了简单工厂模式,具体的Word层来使用工程加工出来的对象,具体的工具封装内容包装在FrameUiautomator和FrameEspresso里。
这里主要说下针对Uiautomator的封装,若是以前读过我写的《如何组织好你的测试代码》,那么里面的一些思想应该比较清楚了,主要的优势就是:
一、页面跳转或者异步加载延迟出现的界面,无需再单独使用sleep;
二、对于系统随机出现的可能会影响App界面的一些因素(例如Android6.0的受权弹框、电话呼入),无需再单独处理;
三、对于App中随机出现的可能会遮挡正常界面的一些弹框,无需再单独处理;
四、全部调用封装后框架的操做,都会记录日志;
五、框架自己有断言能力,若是在框架处理异常状况后还找不到指定控件,这时候会截图而且断言;
六、若是须要替换框架或者框架升级,可使用最小的成原本框架层进行改动,而不须要改动用例层和Word层。
完善其余内容
上面主要讲的是UI自动化的一些行为操做,关于断言的问题,我这里不想说太多,BTV作到界面上的UI元素的检查,以及整个流程是否能够完整的走下来就能够了,若是须要验证数据正确性等一些复杂的内容,能够参考我写的《App任你摆布(反射技术的引入)》。我这里说说UI自动化若是失败了,咱们怎么排查问题?其实很简单我这里作的就是日志+截图。
日志系统最关键的是打日志的时机,这里我把它埋到了BaseTest的execute()中,这样每一次的用例调用AW或者AW中调用KW,均可以记录下来,同时也埋到了框架的具体实现函数中,这样框架只要操做就会记录下日志。这里有个小的技巧,我在打印日志的地方调用了下面的函数:
String classname = Thread.currentThread().getStackTrace()[3].getClassName();
这样就能够记录下当时调用Log函数的当前的类的名称了,这里能够看下我输出的log的样子:
是否是比较齐全了,基本上全部你想知道的信息均可以经过log内容来得到了。下面说说截图,截图和log的总体思路同样,会在一些关键节点埋点同时也支持手动调用。由于个人工具框架是支持自身断言的,所以我在工具框架这一层断言的时候会加入截图,其余地方若是你须要特别关注的时候,也能够手动调用截图触发。
上面的图是否是很是直观,当咱们的用例出现异常错误的时候,直接经过log和日志便可定位到问题的所在。
结果展现
测试结果最终对接了内部的持续集成平台和结果展现平台后是这个样子:
保证了编译器中的结果和结果展现平台中显示的状况一致。
结合实践谈谈
互联网产品的迭代速度之快,各位都深有体会。作为产品质量的保障者,测试人员常常为测试时间不足而烦恼,如何打破现状来让如今变得更好一些,这是咱们一直在思考的问题。软件工程中有提到测试人员越早的介入到研发的流程当中,就能够越早的发现问题,从而下降发现问题的成本。所以"左移"变得很是的有必要了起来,固然左移的方式有不少,例如前几天拜读到的《聊聊测试“左移”那些事》这里面主要讲测试人员经过把控需求来达到左移的效果,而我上面所讲到的内容均可以帮助自动化实现左移的。
想一想以前咱们作的UI自动化是怎么作的呢?在版本提测以后,咱们开始写自动化,这样自动化的主要功能就变成了回归和冒烟。我这里我想说的是在开发写代码的时候,咱们也开始写用例级别代码,在开发定义了界面布局后,咱们就能够完善具体代码,待开发提测时,咱们就能够运行咱们的用例来进行测试了。
固然针对新老版本可能略微有所不一样:
若是是新需求的状况下,咱们在需求肯定的状况下就能够先组织本身的用例了,具体实现依赖开发的word层的代码能够先空着,待开发肯定以后,咱们就能够及时的完善咱们的word层,这样不用等到开发提测以后,咱们才开始设计咱们的自动化测试用例。
对于老的需求变动,一样也是,首先能够看以前的用例中的关键字是否有可复用的东西,若是能够直接复用,那就继续用,若是有新的步骤加进来,那么只须要加入对应的关键字便可,和新需求的作法同样,一样在开发提测以前完成用例的编写。
其实在整个方案启动以前,我就在思考这个问题。那么这个作出来后究竟会有收益吗?由于毕竟作完整个系统不是一件容易的事情,须要花不少的时间成本进去。那么分析收益从哪方面入手呢?我觉定先从bug入手,因而针对最近的一次版本作了一个简单的bug分析:
从数据中能够看到,的确有一部分的bug是能够在左移阶段被发现的。这里分为BVT级别的用例和详细模块的用例。BVT级别用例来限制开发的提测,提测前开发本身去运动这部分用例,经过才能够提测;具体功能级别的详细模块的内容用专门针对这个版本修改或者新增的新功能。
这就是本节想跟你们说的内容,整个过程当中无论是对项目的收益或者是本身的成长,我都有所收货,拿出来和你们进行分享,但愿能帮助到其余人。