对于作web端自动化测试的人来讲,可能接触selenium比QTP还要多,可是咱们在作基于selenium的二次开发的时候,常常会说到二次开发是为了易于维护,不少人可能不懂得维护的价值是什么,和到底要维护什么。今天专门写一篇关于二次开发的文章,但愿可以帮到有须要作二次开发的人。
二次开发也就是咱们常说的封装selenium,或者作框架。可是一个框架要包含丰富的类和方法。要有一套完整的体系来帮助咱们进行封装。能够说框架的设计思想就是整个框架的灵魂,若是设计思想很正确也就意味着这个框架成功了一半,剩下的就是咱们怎么样用程序实现这个思想,在开发的过程当中咱们也许会用到一些设计模式和引用一些开源框架。这些只是一个开发人员或者程序设计者的基本素质。至于若是把selenium可以有效的封装和一些基本思想,咱们来详细的了解一下。
在这篇文章里面只针对selenium的webdriver来进行讨论,咱们再也不对rc作任何的解释和说明。咱们都知道webdriver的使用过程当中,贯穿始终的就是一个driver, 而且这个driver表明了一个浏览器的当前窗口,咱们进行操做的过程当中只是进行当前窗口的操做,也就是最这个current window进行的一系列的操做,若是咱们须要对打开的新的window来进行操做的话,咱们须要switchTo,包括操做frame,固然整个流程下的操做确实让咱们以为不是很难编写,可是咱们编写脚本的过程当中须要用到的一些辅助功能可能就会很难的编写,好比最大化浏览器,视角移动到操做的元素等等,这个过程一次编写咱们能够作到,可是反复的编写的话确定是一个让人很头疼的过程,因此这个时候咱们要去封装一些经常使用的方法,咱们有了作一个比较完整的框架的想法,可是咱们突然又意识到了,这样的话,咱们须要把driver封装起来,由于整个测试的case都是针对的这个driver,而且只有一个driver,这样子的话咱们不容许创造多个的driver,也就意味着咱们要把本身编写的小工具类和driver联系起来,而且咱们的测试用例case类也须要调用这个driver,其实很简单,咱们能够用注入的方式来作,把driver当成tools类的一个属性值,而后注入到咱们的case类中,也能够经过set的方法来进行操做。有了这些基础,咱们能够防止无限的编写重复的方法,这样咱们有了本身的工具类。若是说这就是框架的话,就会显得很是的肤浅,由于咱们写的这些方法根本没有任何逻辑可言,只是把须要的方法通通的堆到了一块儿,因此这个时候咱们须要想一想用到某些方法来进行分一下层次。
Page类和Window类:
PageObject模式咱们都知道,就是把资源都放入到page类里面,而后再编写逻辑类。这样的话就能够无限的复用这些资源,这只是笼统的讲了一下设计的思想,至于PageObject到底怎么去实现这些设计呢?咱们从webDriver的使用开始入手。webdriver是先从定义浏览器开始的。WebDriver driver = new FirefoxDriver(); 这样咱们就定义了一个firefox的浏览器,可是自动化的过程不可能只容许咱们把定义浏览器的操做放在框架代码里面,那样的硬编码方式使咱们的case不存在可移植性了,若是进行兼容性测试的话,维护起来对这些case的修改量是比较大的,这种硬编码方式是咱们不可以进行大量维护的,因此咱们须要把定义浏览器的过程彻底放在case类里面,就是在咱们写测试用例的时候再去编写到底用什么浏览器,防止在编写框架的时候硬编码的形式把浏览器写死在了框架里面。作到多浏览器的可维护性,对于咱们进行兼容测试也有必定的帮助,这样的话咱们须要对浏览器的选择部分要进行必定的编码设计,来完成浏览器的可选择性。在咱们定义完了浏览器以后,这个时候咱们也许以为就是开始查找元素了,可是在这个driver的基础上咱们应该发现其实这个时候driver表明的整个页面的操做。可是在页面的操做基础上咱们应该意识到还有一个级别的操做,那就是window的操做,就是针对浏览器自身的操做。包括一些基本的返回,向前,最大化,最小化,或者移动到制定元素的位置,调用js等等等,这些方法的级别是出于window级别的,和页面无关的。因此咱们应该把这些全部的方法都封装到单独的一个层次中,咱们暂且称之为window包中,刚才的浏览器的选择的全部方法咱们放browser包中。这样咱们设计出了两个层次。下面的设计该如何进行呢?咱们知道pageObject的思想是把资源都放入到咱们定义的page类里面,因此这个时候咱们须要思考了,咱们如何设计这里的page类呢?按照pageObject的思想来看,page类应该是咱们本身编写的,那样咱们的框架是否是就能够放弃编写page类了呢?直接封装一些通用的方法?显然是不对的,对于页面html源码操做的过程当中,咱们烦透了这些元素查找的硬编码方式,在一个case里面或者两个case里面反复的调用编写是让人头疼的一件事情,因此咱们能够把全部的单页面看作一个层次,里面和PageObject的思想同样,就是放入了通用的方法,可是它存在的意义不仅是这样简单,由于咱们操做的过程当中须要涉及到frame。而且页面和页面之间涉及到不一样window之间的切换,因此如何协调window和page之间的关系成为了咱们须要注意的难点和重点,咱们知道webdriver里面有一个方法叫作getWindowHandlers,这个方法能够得到全部的句柄,咱们想设计这个page类那么意味着咱们须要去完美的配合window类,他们之间惟一的关联就是这个方法,因此咱们能够设计一个概念,叫作页面集合,在建立window对象的时候咱们就会自动的出现一个页面集合器,它的功能就是管理全部的在操做过程当中打开的页面。而且可以指定一个特殊的page,就是当前页面。也就是webdriver中的driver对象,由于它一直都是针对当前页面编程的。那样咱们的window类里面就存在了两个属性,一个收集器,一个当前页。这样咱们在window的级别上就可以彻底操做page类了,这样咱们在case类设计的时候,只须要经过window类的级别进行编码就能够了,彻底能够把page类看成一种资源来处理,咱们全部须要的东西都是经过page来获取的。page类的设计中咱们必定要编写通用的方法。好比获取title等等等,最主要的一点就是要进行frame的处理操做,咱们如何把frame完美的结合在page里面呢?咱们在普通的webdriver脚本编写过程当中可能反反复复的switchTo的方法让咱们很恼火,而且很难让咱们理解这些脚本究竟是想表达什么意思呢?因此咱们须要进行对page进行层次上的小分级,就是把page类再分为一个frame的类和一个模块的类,由于一个大型页面里面,通用的结构和模块是不少的。咱们单独的定义一个模块类,可让这些相同的结构复用在里面的方法。定义frame类主要是处理把定位到frame的操做从case类中脱离出来,咱们编写到page类里面或者frame类里面,提供一种方式或者方法来进行frame的定位就能够了,简单实用,而且简化case类的编写,毕竟case类并非由设计者来编写,尽可能作到最简化。固然咱们能够成这整个层次都是page类,放在page包里面。
Element类:
Element类就是咱们经常使用的driver.findElement()的那种形式,就是元素类,什么是元素类呢?元素就是咱们须要定位的那些东西,咱们在操做过程当中很难知道一个findElement到底查找的是什么,由于全部的标签形式都是经过id或者各类各样的定位方式来实现的。这个时候咱们须要把各类各样的findElement都通通的放在一块儿,形成的脚本难以理解让后续接手的脚本开发人员可能头疼不已,因此咱们能够作一些简单的加工。固然咱们还要作的一个最大的工做就是元素查找的封装。Element和Page类如何关联起来。咱们知道page和webElement的关联就是一个driver.findElement的方法。这样就能够在页面上查找元素了。因此咱们也在element类中经过这种形式来进行他们之间的关联。咱们经过在element类中添加定位方式的形式来进行元素定位和page的关联,咱们只须要在编写本身的page类的过程当中直接加入element类就能够了,element类提供了全部的关于element的方法,好比鼠标事件和键盘事件,还有更重要的元素定位方法。定位的方法在这里不作任何的推荐,由于每一个人的思路不一样,实现的方式也不一样,我我的比较偏向的作法是作一个xml来进行资源的管理,把全部须要的资源都放入到xml里面,这样咱们就能够进行元素的定位了。而且在后期维护中主要维护xml就能够进行对整个脚本进行维护了,不须要咱们大量的从新进行源码的分析和修改了。固然这是设计的优化过程,由于定位的实现咱们仍是须要本身来完成的,咱们知道元素的定位方式各类各样,咱们怎么来进行管理和定位呢?咱们能够经过map的方法做为属性值来进行元素的管理,他的各类定位方法存放在map中,咱们须要的时候只须要调一下就能够了。经过观察源码findElement也是经过map的形式来进行元素定位存储的。咱们能够借鉴一下源码的实现方式。固然咱们彻底封装findElement也是能够的。说到这里可能咱们有一个问题比较难以解决,那就是层级定位。若是咱们只是给element类添加定位方式的话,那么findElement提供的一级一级的定位方式咱们就没法应用了,因此在element类中咱们必要还要提供findElement的方法进行层级定位。这只是为了把webdriver的全部方法都尽可能应用到而已。其实经过xpath的方式咱们就能够基本上定位大多数的元素。剩下的内容就是咱们对element内容的扩充了。咱们能够把元素的更加具体的抽象出来,好比咱们把select,table,checkbox等等等的各类html标签元素显式的定义出来,在咱们定义一个元素的时候咱们可以更加清晰的看到这个元素的含义,咱们知道它是一个按钮或者table等等等,这些小元素的操做须要咱们本身深刻理解和开发,这里不作过多的介绍。
其余类:
经过这些层次的分析咱们已经出现一个框架的雏形了,而后咱们剩下的设计就是基于完善和优化了。在一个自动化过程当中case类是很是重要的,咱们须要知道case类运行结束的结果报告和分析,因此case类的运行等等一系列的东西咱们都得有统计,这些东西必需要咱们提供一些类来实现,不过所幸的是,强大的junit或者testng彻底能够取代咱们要去作的工做,他们能够很完美的提供这些功能,咱们只须要介入这些开源包就能够了。咱们使用QTP的过程当中咱们常常会用到参数化,咱们自动化的设计都有了,可是没有参数化的功能怎么办?咱们应该先想象一下数据提供的方式。testng提供了一种参数化的形式,可是它是须要在xml里面配置或者硬编码的形式来进行编写。不过它提供了一种dataprovider的方式来进行参数化,它传递参数的形式是Object[][],咱们可能但愿使用参数的时候经过excel表格来完成,这些都是能够实现的,poi包提供了解析excel的功能,很是的强大。咱们能够本身编写解析类来进行参数化的功能编写,具体实现再也不过多去说。调用的方式就简单多了,硬性的记住几个注解就能够了。case类的各类运行咱们都有了,还须要一些什么扩展呢?很显然就是日志的扩展。日志的设计也是颇有技巧的。咱们须要用日志监控某一些方法的话,若是前期没有直接加入日志功能,咱们能够经过spring的方式来进行日志切入操做。可是咱们在观察日志的时候咱们一般会但愿知道到底运行到哪一步了,咱们能够想想,全部的操做都是基于element或者window的,page的只是一个抽象出来的概念,因此咱们只须要把日志加入到每个element的方法和window的主要方法里面就能够监控到整个运行的过程,毕竟咱们不可以去亲自盯着屏幕一直。这样没个方法不外乎就是运行成功和失败,因此咱们能够经过这种方式来进行编码。日志的实现咱们能够经过经过的log4j或者本身编写一个小的日志系统。都是可行的方案。
扩展类:
也许咱们须要这些系统可以有良好的可移植性,咱们能够本身编写类加载器,为之后作整个自动化的测试平台作准备。最主要都是咱们作的这些操做可能须要越来简单,因此咱们可能会由于引入注解的方式来提供编码效率,因此咱们还须要为注解类作一些辅助的工做。固然这些注解的开发须要咱们作足足够的需求研究,并非无谓的去开发各类注解。这些注解的应用应该说很普遍,再也不多说注解的好处。而且注解还有一点可应用的地方就是放在数据库的操做中,在自动化测试中,其实数据库的测试也是一个大的难点。在这里咱们只讨论前端自动化的设计,不过多的讨论别的东西。
前面讲到的这些类的存在形式其实就是在框架里面的一种层次,咱们谈论的这些都是基于webdriver的,而且主要基于前端自动化测试的,固然自动化测试不仅包括这些,还包括服务器端,接口自动化,单元自动化等等。我我的的能力水平也是颇有限,可能不少地方说到的不是很到位,但愿可以经过这篇文章可以给那些但愿学习自动化,但愿编写小测试框架的童鞋,一点点的启发。若是真的有须要想了解的更多,能够留言给我~~互相交流~html