一, 设计背景
随着 IT 行业的发展, 产品愈渐复杂, web 端业务及流程更加繁琐, 目前 UI 测试仅是针对单一页面, 操作量大. 为了满足多页面功能及流程的需求及节省工时, 设计了这款 UI 自动化测试程序. 旨在提供接口, 集成到蜗牛自动化测试框架, 方便用例的设计.
整个程序是基于 selenium 设计的. 程序对 selenium 提供的接口进行了二次封装以满足日常的用例设计, 二次封装后的接口解决了元素加载, 元素定位解析等问题, 可以让用例设计变得更加简捷.
之所以采用 Selenium 的模式. 原因一, 对于用户来说这是一个开源框架, 很想窥探一二; 原因二, Selenium 可无缝接入. 这是一个用于 Web 应用程序测试的工具, 支持多平台, 多浏览器, 多语言去实现自动化测试, Selenium2 将浏览器原生的 API 封装成 WebDriver API, 可以直接操作浏览器页面里的元素, 甚至操作浏览器本身 (截屏, 窗口大小, 启动, 关闭之类的), 所以就像真正的用户在操作一样.
目前支持: Mac,Windows 操作系统, Chrome,Firefox,IE 浏览器.
二, 工作原理
在蜗牛管理后台添加测试用例.
蜗牛管理后台测试用例执行调用任务执行接口, 传送任务 id 及测试数据的 JSON 格式字符串给程序.
程序根据获取数据, 解析并处理.
启动浏览器后, selenium-webdriver 会将目标浏览器绑定到特定的端口, 启动后的浏览器则作为 webdriver 的 server.
客户端 (也就是测试脚本), 借助 ComandExecutor 发送 HTTP 请求给 server 端 (通信协议: The WebDriver Wire Protocol, 在 HTTP request 的 body 中, 会以 WebDriver Wire 协议规定的 JSON 格式的字符串来告诉 Selenium, 我们希望浏览器接下来做什么事情).
Server 端需要依赖原生的浏览器组件, 转化 Web Service 的命令为浏览器 native 的调用来完成操作.
最后将处理结果及任务 id 通过 JSON 字符串的格式返回给蜗牛, 通过蜗牛的管理后台可查看每条用例执行结果.
三, 框架介绍
3.1 工程结构
按照实际的业务流程调用对应接口来实现 Web-UI 自动化测试用例. case 层可调用 service 层和 pageObject 层的接口, pageObject 是对每一个页面元素的一个封装, service 是对一个常用的业务模块功能的封装. 比如一个查询企业信息的测试用例, 需要依赖登入, 这个业务功能就可以直接调用 service 中的接口. 企业查询的创建就可以调用 pageObject 中的接口, 然后按照查询的业务流程, 在测试用例中把这些接口串起来就形成了一个 UI 自动化测试用例, 详细细节接下去会举例说明.
如企业查询. 查询之前, 需要登入管理后台, 登入操作已封装到业务层, 直接调用 service 层的接口, 不需要在意这个步骤的细节; 登入之后要指定一个路径, 找到对应的空间, 直接调用 model 层的接口, 不需要在意这个步骤的细节; 接着是创建查询, 创建查询的所有定位方法也封装到业务层, 这就是个企业查询的实现, 也是用例设计中最主要的环节.
整个工程基于 selenium, 采用 pageObject 模式搭建. 下面对工程中的几个重要模块做介绍.
3.1.1 driver - 接口层
对 Web 页面所有元素的操作都是在 driver 定义接口并实现的. driver 对 selenium 提供的接口做了二次封装, 对外提供封装后的接口. pageObject 实现了一些公共方法, 比如给输入框赋值等, 目前 pageObject 封装的方法不多, 大多功能都可以通过 selenium 实现. driver 层对开源工具接口做了二次封装, 想要驱动一个浏览器还有一个必不可少的工具 -- 浏览器驱动, 这个驱动放在 Referenced Libraries 里, 驱动的版本必须与被测浏览器版本相匹配.
3.1.2 model - 数据模型
创建数据模型为了实现测试数据和测试用例分离而采取的一种方法, 具体的测试数据初始化. 可以对一个业务流程中需要测试数据的元素在一个 model 中定义出来, 方便管理和代码阅读.
3.1.3 pageObject - 业务层
pageObject 模式, 采用接口形式封装每一个页面需要用到的元素, 实现封装只要做两步:
确定元素的定位方式;
调用 driver 中对应的操作接口.
driver 的接口实现包含了一定的容错能力, 但并不是全面的, 部分页面或者组件具有独特性, 单纯调用 driver 的接口并不能保证测试用例的稳定性, 此时就需要在 pageObject 的接口实现中加入一些容错算法, 确保用例稳定性.
3.1.4 service - 提供业务功能
一个业务流程很多时候依赖其他业务模块功能, 为了方便设计一个测试用例, 也为了避免重复造轮子, service 层就提供了一些常用的业务功能, 比如登入, 企业查询等. 依赖方只需要在 service 层调用即可.
3.2 功能优化
对 selenium 做二次封装的同时也对接口做了优化, 框架的初衷是使 UI 用例的设计尽可能的易设计, 易读, 易维护.
3.2.1 接口优化
直接调用 selenium 的接口经常会遇到些令人头疼的问题, 比如网络问题使页面 loading 太慢, 需要操作的元素还没展示出来, 这种情况就会经常报元素找不到的 error, 导致用例执行失败, 但实际上这种报错并不是一个 bug, 其测试结果是无效的. 为了减少误报率 driver 层接口设计了等待元素加载的功能, 使用的关键方法: cf.searchForElementVisibleXpath(TestStartQuitwd.wd, "//*[text()='运营平台登录']", id, 200, 100L). 参考代码:
在 click,input 等操作接口中加入循环查找的判断可最大限度的等待一个元素的加载从而提高测试用例的稳定性.
3.2.2 元素定位统一入口
接触过 UI 自动化用例设计的测试人员会比较清楚, 想通过 selenium 操作一个元素, 其中必不可少的就是对元素定位的描述, 通俗的讲就是要通知接口在当前页面操作哪个位置上的元素. 定位一个元素的方法很多, 常用的有 id,name,CSS,xpath 等, 对应不同的定位方法 selenium 在处理上也给出了不同接口, 这从维护角度上来考虑显然不是最好的. 最好的做法就是用例设计者只管元素定位和操作事件的调用, 而事件在实现上走了哪种渠道最好是无感知, 无需维护的. 对此框架封装了一个方法供 driver 调用, 主要功能就是解析描述元素的字符串自动判断是 id,CSS 还是 xpath.
3.3 元素定位
UI 自动化用例其实可以分成两部分: 定位元素; 调用接口操作该元素. 其中定位一个元素的方法很多, 常用的有 id,name,CSS,xpath. 实际设计中选择哪种定位方法一般会在维护角度上考虑的会多一些, 因为现在的服务器性能配置等都很优秀, 所以跑一个 Web-UI 用例可以不用考虑性能问题. 从维护成本上考虑会优先选择 id,name, 其次 CSS, 最后用 xpath.
我们不能保证每一个 Web 系统的所有元素都能提供一个唯一 id 或 name, 当然如果能和前端开发达成合作, 这就是一件很美好的事情了. 一般情况下我们都需要面对没有 id 和 name 这两个属性的情况. 这时我们就可以使用 CSS 样式, 很多时候 CSS 样式是能满足我们的定位需求. 当然在这些都不提供给我们的情况下就只能选择 xpath, 使用 xpath 的优点:
易获取, 主流浏览器只要打开 "查看" 就可以通过 copy 轻松获取到;
页面上的元素都可以用 xpath 来描述; 缺点, 不稳定, 大量使用会给用例维护产生很大的负担.
xpath 一般只要前端在页面上做一下小调整, 用例就必须重新维护, 在不得不使用 xpath 的情况下, 为了减少今后的维护量, 可对 xpath 做一些优化, 可以减少 xpath 的路径长度提高稳定性. 以下是实践过程中最长用到的几种类型:
依靠自己的属性文本定位, 如 //input[@value='XXXXX']
包含指示性字符, 如 //input[contains(text(),'指示性字符')]
巧妙使用 content, 如 //*[@id='app-container']
四, 常见报错
使用过程中经常会遇到问题, 这里做下总结方便 debug.
某些页面弹窗, 有时候定位不到弹窗元素. 理论上 selenium 在一个页面中查找一个元素是可以定位到, 但有些时候出现弹窗, 此时就需要在重新定位弹窗. 解决方法:
有些输入框不能被 input 接口正常操作. 实践过程中在日历控件中遇到过, 元素定位什么的都对, 但就是不能正常被操作. 解决方法: 判断元素是否是 select 类型, 之后再赋值. 解决代码:
3. 发现 selenium 的某些接口不能 work 了, 此时最大的可能就是浏览器升级了. 解决方法: 重新下载低版本浏览器.
4. 元素不可见. 有一种元素能在页面上正常展示, 但对于工具来说它是不可见的, 这是因为在一般情况下, 元素可见需要满足以下几个条件: visibility!=hidden ; display!=none; opacity!=0; height,width 都大于 0; 对于 input 标签, 没有 hidden 属性. 如截图就是只读的实例.
解决方法: 调用接口 TestStartQuitwd.JS.executeScript("var txtN = document.getElementsByName("timeRange"); txtN[0].readOnly = false;");
五, 结束语
UI 自动化是在开源工具的基础上做了些优化, 在 driver 层, 数据层, 业务层以及用例层的解决方案还有很大的提升空间. Web-UI 自动化还不完美, 后期还需继续努力. 感谢一直以来支持研究的小伙伴.
来源: https://www.cnblogs.com/yixinjishu/p/11491856.html