有赞 WEB-UI 自动化实践

概述

Bee 是由有赞 QA 开发的 UI 自动化工具,并以此实现了 web 端和 wap 端的核心业务的自动化。旨在简化开源工具提供的接口,方便 UI 自动化测试用例的设计。css

Bee 整个框架是基于 selenium 和 selenide 设计的。框架对 selenium 和 selenide 提供的接口进行了二次封装以知足平常的用例设计,二次封装后的接口解决了一些元素加载,元素定位解析等问题,可让用例设计变得更加简便。 Bee 能支持 Web 和 Wap 页面的元素定位以及操做,其中 Selenide 主要支持 Web 页面的元素操做,Selenium 支持 Wap 页面的元素操做。html

Bee 为何会采用 Selenide+Selenium 的模式。缘由一,其实框架设计的初衷是想所有依赖 Selenide 来完成 Web 和 Wap 的自动化,Selenide 对于做者来讲是一个全新的开源框架,很想窥探一二; 缘由二,Selenium 可无缝接入。在实践过程当中发现 Selenide 还不能支持 Wap 页面,知足不了平常的测试需求,好在框架能够很容易的嵌入 Selenium 从而实现了 Wap 页面的自动化,也正是 Selenide 和 selenium 有这个特性,因此在框架设计初期才敢放心的尝试采用Selenide;缘由三,在实践中的切身体会 Selenide 对页面元素的处理会比 Selenium 平滑的多,由于 Selenide 其自己也是对 Selenium 的一个二次封装,对 Selenium 的接口也作了不少的优化。前端

Bee 目前支持的环境为:mac、chrome,整个框架支持可扩展。git

对于 Selenide 和 Selenium 的原理不在本文中赘述,你们能够到网上学习了解。 Bee 开源地址:beeyz,欢迎交流。github

用例设计

按照实际的业务流程调用对应接口来实现 WEB-UI 自动化测试用例。case 层可调用 service 层和 pageObject 层的接口,pageObject 是对每个页面元素的一个封装,service 是对一个经常使用的业务模块功能的封装。好比一个营销秒杀的测试用例,须要依赖登入、建立商品,这两个业务功能就能够直接调用 service 中的接口。秒杀活动的建立就能够调用 pageObject 中的接口,而后按照秒杀的业务流程,在测试用例中把这些接口串起来就造成了一个 UI 自动化测试用例,详细细节接下去会举例说明。

设计用例的灵活度取决于 pageObject 封装的颗粒度,颗粒度越小越容易在用例层设计出新流程的测试用例。用例层使用了 testng,可按照实际的需求灵活设计一个测试用例。推荐在封装 pageObject 接口的时候,颗粒度定义的越小越好,方便用例的扩展和维护。pageObject 封装的接口就至关于一个原子,原子粒度越小越方便组装成新的用例。相反若是粒度太粗维护上会不太方便。参考代码:web

截图是一个秒杀用例。建立活动以前,须要登入有赞微商城后台,登入操做已封装到 loginService,直接调用 service 层的接口,不须要在乎这个步骤的细节;登入以后要指定一个商品参与秒杀活动,普通商品建立已封装到 goodsService,直接调用 service 层的接口,不须要在乎这个步骤的细节;接着是建立秒杀活动,建立秒杀活动的全部业务步骤都封装到 seckillPage,这就是个 pageObject 的实现,也是用例设计中最主要的环节。最后把这几个步骤串起来就造成了一个秒杀活动的测试用例。用例结构清晰,组装简单。算法

框架介绍

一、工程结构

整个工程基于 selenide & selenium,采用 pageObject 模式搭建起来。技术结构:selenide+selenium+testng+reportng+spring。下面对工程中的几个重要模块作介绍。spring

1.1 dataprovider — 数据层

为了实现测试数据和测试用例分离而采起的一种方法,数据模型在 model 中定义,具体的测试数据则在 dataprovider 中初始化。chrome

1.2 driver — 接口层

对 web 页面全部元素的操做都是在这里定义接口并实现的。driver 对 selenide&selenium 提供的接口作了二次封装,对外提供封装后的接口。common 实现了一些和接口相关的公共方法,好比模拟键盘按钮等,目前 common 封装的方法很少,大多功能均可以经过 selenide&selenium 实现。driver 层对开源工具接口作了二次封装,想要驱动一个浏览器还有一个必不可少的工具 —— 浏览器驱动,这个驱动放在 resources 里,驱动的版本必须与被测浏览器版本相匹配。shell

1.3 listeners — 监听器

为了提升框架自己的容错能力监听一些事件。目前实现了:1. 监听用例测试结果,可对不一样的测试结果监听器作不一样的处理;2. 失败测试用例重试的监听,一个 fail 的用例最多可重试3次。

1.4 model — 数据模型

为了实现测试数据和测试用例分离而采起的一种方法,具体的测试数据在 dataprovider 中初始化。能够对一个业务流程中须要测试数据的元素在一个 model 中定义出来,方便管理和代码阅读。

1.5 pageObject — 业务层

pageObject 模式,接口形式封装每个页面须要用到的元素,实现上只要作两步:肯定元素的定位方式;调用 driver 中对应的操做接口。driver 的接口实现包含了必定的容错能力,但并非全面的,有些页面独特性或者组件的独特性单纯调用 driver 的接口并不能保证测试用例的稳定性,此时就须要在 pageObject 的接口实现中加入一些容错算法,确保用例稳定性。 实际操做的经验是 pageObject 对元素封装的颗粒度越小,在用例设计层设计测试用例就越灵活,能够像组装工具那样组装出一个新的测试用例。参考代码:

1.6 service — 提供业务功能

一个业务流程不少时候依赖其余业务模块功能,为了方便设计一个测试用例,也为了不重复造轮子,service 层就提供了一些经常使用的业务功能,好比登入、建立商品等。依赖方只须要在 service 层调用便可。

二、功能优化

Bee 对 selenide&selenium 作二次封装的同时也对接口作了些优化,框架的初衷是使设计一个 UI 用例尽量的易设计、易读、易维护。

2.1 接口优化

直接调用 selenide 或者 selenium 的接口常常会遇到些使人头疼的问题,好比网络问题使页面 loading 太慢,须要操做的元素还没展现出来,这种状况就会常常报元素找不到的 error,用例执行失败,但实际上这种报错不是一个 bug,测试结果是无效的。为了提升误报率 driver 层接口实现了等待元素加载的功能,使用的关键接口:Selenide.$(elementLocator).waitUntil(Condition.appears, timeout)。参考代码:

`/**
     * 检查元素加载状况
     * @param elementLocator
     * @param timeout
     * @return
     */
    private boolean checkElementLoad(By elementLocator, long timeout){
        try {
            Selenide.$(elementLocator).waitUntil(Condition.appears, timeout);
            return true;
        }catch (Exception ex){
            throw new RuntimeException(ex);
        }
    }
/**
     * 若是没有找到元素抛null异常
     * @param element
     * @param timeout
     * @param elementType
     * @return
     */
    private By isElementLoaded(String element, long timeout,String ...elementType){
        By elementLocator = null;
        int count = 4;
        long partTimeout = timeout/count;
        for(int i=0; i<count; i++) {
            elementLocator = waitingForElementLoad(element, partTimeout, elementType);
            if(null != elementLocator){
                break;
            }else if (null == elementLocator && (count-1) == i) {//元素为null抛出异常
                log.error("Web页面元素:{} 没法定位",element);
            }
        }
        return elementLocator;
    }`
复制代码

概述中提到过 selenide 自己就是对 selenium 的一个二次封装,因此 selenide 对元素的操做会比 selenium 平滑的多。在页面加载方面 selenide 自己有作优化,再在 click、input 等操做接口中加入 waitUntil 的判断可最大限度的等待一个元素的加载从而提升测试用例的稳定性。

2.2 元素定位统一入口

接触过 UI 自动化用例设计的同窗会比较清楚,想经过 selenide&selenium 操做一个元素,其中必不可少的就是对元素定位的描述,通俗的讲就是要通知接口在当前页面操做哪一个位置上的元素。定位一个元素的方法不少,经常使用的有 id,name,css,xpath 等,对应不一样的定位方法 selenide&selenium 在处理上也给出了不一样接口。这从维护角度上来考虑显然不是最好的。最好的作法就是用例设计者只管元素定位和操做事件的调用,而事件实现上走了哪一种渠道最好是无感知,无需维护的。对此框架封装了一个方法供 driver 调用,主要功能就是解析描述元素的字符串自动判断是 id、css 仍是 xpath。

2.3 失败测试用例重试

网络缘由等不肯定因素会致使测试用例失败,这种外部因素致使的失败通常都会认为它是无效的,为了提升测试报告的可信度,增长了失败用例重试的机制。具体作法是实现一个用例测试结果的监听器,当监听器监听到一个 fail 的结果,会触发重试,失败用例最多重试 3 次。

三、元素定位

UI自动化用例其实能够分红两部分,1. 定位元素;2. 调用接口操做该元素。其中定位一个元素的方法不少,经常使用的有 id,name,css,xpath。实际设计中选择哪一种定位方法通常会在维护角度上考虑的会多一些,由于如今的服务器性能配置等都很优秀,因此跑一个WEB-UI用例能够不用考虑性能问题。从维护成本上考虑会优先选择 id、name,其次 css,最后用 xpath。

咱们并不能保证每个 web 系统的全部元素都能给你提供一个惟一 id 或者惟一的 name,固然若是能和前端开发达成合做这就是一件很美好的事情了,通常状况下咱们都须要面对没有 id 和 name 这两个属性的状况。这时咱们就可使用 css 样式,不少时候 css 样式是能知足咱们的定位需求。固然在这些都不提供给咱们的状况下就只能选择 xpath,使用 xpath 的优势 1. 易获取,主流浏览器只要打开“查看”就能够经过 copy 轻松获取到;2. 页面上的元素均可以用 xpath 来描述;缺点,不稳定,大量使用的话会给用例维护产生很大的负担。 xpath 通常只要前端在页面上作一下小调整用例就必须从新维护,在不得不使用 xpath 的状况下为了减小从此的维护量可对 xpath 作一些优化,能够减小 xpath 的路径长度提升稳定性。如下是实践过程当中最长用到的几种类型:

  1. 依靠本身的属性文本定位,如 //input[@value=‘XXXXX’]
  2. 包含指示性字符,如 //input[contains(text(),’指示性字符’)]
  3. 巧妙使用 descendant,如 //*[@id=‘app-container']/descendant::input

CI集成

用例设计完成以后就能够加入集成建设,让UI自动化用例在集成环境中发挥做用。测试报告展现使用 reportng。jenkins 的插件能够很好的把 report 呈现出来,因此 reportng + jenkins 是一个很不错的组合。

搭建的步骤:

  1. 搭建一个 jenkins。
  2. 一台用于跑 UI 自动化用例的服务器。
  3. 将服务器配置成 jenkins 的一个节点。
  4. jenkins 建立 job,job 中须要使用的插件包含 Git、Excute shell、Editable Email Notification、Publish HTML reports。其中 editable email notification,支持邮件提醒,是个很不错的插件。支持 html report 格式,附件功能。

常见报错

使用 Bee 过程当中常常会遇到些问题,这里作下总结方便 debug。

  1. 某些页面不滚动。有时候一屏展现不了全部的元素。理论上 selenide 或者 selenium 在一个页面中查找一个元素是能够自动执行滚屏,但有些时候滚屏会失效,此时就须要在测试用例中实现滚屏查找元素。 解决方法:void scrollToElement(String element,String …elementType)
  2. 有些输入框不能被 input 接口正常操做。实践过程当中在日历控件中遇到过,元素定位什么的都对,但就是不能正常被操做。 解决方法:void triggerInput(String element,String …elementType),该接口起到一个触发的做用,实际操做中遇到相似的状况能够把它当作一种尝试。
  3. 按钮不能被 click 接口正常操做。button 元素定位彻底正确。且在“检查”窗口中看到的也是 button 属性。 <button type="button" class="zent-btn-primary zent-btn-large zent-btn">肯定</button> 解决方法:调用接口 void clickByText(String text)
  4. 发现 selenide 或者 selenium 的某些接口不能 work 了,此时最大的可能就是浏览器升级了。 解决方法:升级浏览器驱动
  5. 元素不可见。有一种元素能在页面上正常展现但对于工具来讲它是不可见的,这是由于在通常状况下元素可见须要知足如下几个条件:visibility!=hidden ; display!=none; opacity!=0; height、width都大于0;对于 input 标签,没有 hidden 属性。如截图就是 opacity=0 的实例。

解决方法:调用接口 void clickByJs(String element,String ... elementType)

结束语

Bee 是在开源工具的基础上作了些优化,目前为止 Bee 更多的是在 driver 层作了些努力,数据层、业务层以及用例层的解决方案还有很大的提高空间。实现一个 WEB-UI 自动化用例主流的方法有录制和代码实现这两种,其实两种方法各有优劣。

Bee 还不完美,后期还需继续努力。感谢一直以来支持 Bee 开发的小伙伴,有你有赞,有你有 Bee。

相关文章
相关标签/搜索