The web has evolved. Finally, testing has too.
事实上对于 UI 自动化测试来讲,许多所谓框架之间并无太多差异,也历来不是影响整套测试用例是否健壮的关键性因素。相比之下,如何提升测试用例稳定性以及出现错误时 debug 的便捷性才是让 UI 自动化测试方案落地的重要细节。前端
那么为何咱们还须要讨论技术选型呢?首先咱们来看看技术选型包含哪些部分。java
一般 UI 自动化测试的技术方案分为控制(控制客户端)、执行(运行经过特定 API 编写的测试用例)、结果上报这几个主要组成部分,在过去各种框架每每喜欢在执行和结果上报两个部分提供差别化的 API 来提升开发效率,但这很难对咱们开头提到的两个重要细节起到本质上的帮助。
随着 Web 技术的不断演进,Web UI 自动化测试中的控制部分也终于有了更进一步的发展,并且这一部分正是解决用例稳定性、提高 debug 能力的核心所在。linux
接下来就对比一下目前可选的几种控制方案的优缺点。web
selenium 的方案最为传统,也是目前最多见的浏览器控制方法。selenium 一般须要和 webdriver 配合使用,selenium 经过 webdriver 控制浏览器,再对上层执行层暴露 API 或 sdk。
同时 selenium 也提供 standalone server 的方案,容许执行层经过调用标准 restful API 控制浏览器,在这种模式下对执行层的编程语言和运行时都没有任何限制,这也是 selenium 生态繁荣的重要缘由。chrome
selenium 的 API 封装遵循 W3C 提供的 webdriver 标准,所以 selenium 对各大主流浏览器的支持都不错,若是测试场景对浏览器兼容性有较高的要求,须要在多种浏览器中执行测试用例,selenium 还是首选。
同时因为 selenium 已经发展多年,各类解决方案也更为完善。例如并行方案 selenium grid,能够支持多节点的用例负载均衡;还有在 CI 场景下官方维护的各类 docker image 等。docker
selenium 是一套较重的方案,选择 selenium 意味着依赖 webdriver、java,若是执行层的编程语言不是 java,那么额外引入这些无疑是较为痛苦的。
另外一方面,selenium 为主的方案通常会造成一个“测试用例 -> 测试框架 -> selenium -> webdriver -> 浏览器”的复杂控制链,链上每多一环就意味着 debug 的复杂度上升一级,这会让测试用例的编写成本显著上升。编程
chrome devtools protocol(如下简称 CDP)能够看做 chrome(或其余使用 Blink 内核的浏览器)开放的远程控制协议。经过 CDP 咱们能够控制 DOM、Debugger 和网络请求等浏览器内部领域,从而实现测试的目的。
相比之下 CDP 不依赖 webdriver 这样的二进制文件,也不绑定特定的编程语言,理论上能够直接在测试用例文件中和 CDP 通讯,控制浏览器。可是实际上已经有不少成熟的 CDP 封装,大部分状况下咱们使用它们而不是直接和 CDP 交互。
对 CDP 的封装能够大体分为两类,一类是只对网络通讯部分封装,而不涉及启动浏览器、管理浏览器进程等工做。例如 chrome-remote-interface 就属于这类,只提供了良好的 JS API 封装,若是须要知足自动化测试的需求还须要搭配 chrome-launcher 这样的库对浏览器进程进行可编程的管理。
另外一类则是一体化的方案,最佳实践是 google 本身维护的 puppeteer 项目。puppeteer 不只负责管理浏览器和将 CDP 封装成 high level 的 API,甚至还默认下载一个指定版本的 chromium 并使用,以免本地浏览器版本不肯定带来的稳定性隐患。redux
做为控制部分,CDP 的方案每每控制链较短,而且对编程语言没有限制,相对而言会更加稳定,而且也带来更好的执行速度。
chrome 在 59 版本开始引入了 headless 模式,这对 CI 流程来讲有很大的帮助。过去若是须要在 linux 服务器上运行 Web UI 测试,一般都须要引入 xvfb 模拟屏幕,headless 模式则能够省去。
除此以外,CDP 相比 webdriver 标准控制能力更强,例如能够对网络层进行监听,从而能够轻松实现“全部网络请求完成后开始交互”这样的功能。后端
CDP 方案只能兼容 Blink 内核的浏览器,所以对浏览器兼容性有要求的测试不该该选用。
不管是 CDP 仍是更进一步的 puppeteer,目标都是实现一个通用的自动化工具,而不是聚焦于测试。所以其内部没有集成测试相关的功能,例如断言、用例编排、结果上报、执行负载均衡等等,须要继续引入第三方库或自行实现,这做为一个测试框架而言不够理想。浏览器
Inject script 的方式是指在浏览器打开的 Web 应用内注入测试引擎、测试用例等脚本,将测试用例执行在被测试应用的运行时中。
Cypress 和 Testcafe 这两个测试框架是这类控制方式的实践者,它们认为过去许多依托于 selenium 构建的测试框架的核心问题在于都是从外部控制浏览器和 Web 应用,执行命令或者获取信息都须要经过网络请求进行交互,所以交互的信息须要进行序列化。这不只限制了交互的内容,还对 debug 带来了极大的不便,同时网络请求带来的开销也让测试变得更加缓慢。
与之相反的是 inject script 选择从内部控制浏览器,测试用例代码将和被测试的 Web 应用运行在同一个浏览器运行时中,能够理解为注入的脚本即为测试客户端,与后端创建通讯,全部的操做指令都是经过 Javascipt 实现并执行,本质上只是函数的调用,客户端和后端之间的通讯仅用于测试结果的收集,不包含具体的指令执行。
值得一提的是 selenium 1 中最先采用的也是相似 inject script 的实现,可是因为当时各个浏览器中的 Javascript 运行时缺少统一的标准,执行结果不一致,所以产生了不少兼容性问题,selenium 才不得不经过标准更规范、而且有厂商支持的 webdriver 来进行控制。但时至今日,Javascript 规范已经很是成熟,主流浏览器的 Javascript 内核也都严格遵循规范予以实现,inject script 方案最大的弱点也就得以克服。
Cypress 和 Testcafe 的定位是完整的测试框架,而且只聚焦于完成 UI 自动化测试这一个任务,内部包含执行框架、断言、请求 mock、结果上报等全部测试所需功能,不须要再配置其它的依赖项。
因为控制流程的改进,双方在测试稳定性、运行速度方面都有很大的改善,能够被视做是下一代 UI 测试框架,可是二者也由于目标场景略有不一样而在一些方面各有优劣,须要根据实际使用场景进行选择。
用例执行的稳定性在两个框架中都做为最高优先级加以解决。许多 UI 测试的不稳定性来源于不能很好的处理界面中的异步状况,而 Cypress 和 Testcafe 在指令的 API 设计中就默认全部指令均有异步状况,会自动完成 wait 的处理。
此外同一运行时的设计在测试一些现代前端框架构建的应用时有巨大的优点,例如直接测试框架数据状态管理(如 redux)的正确性等等,支持编写一些更偏向白盒测试的用例。
双方也都有官方支持的 CI 集成方案,保证在 linux 服务器环境下也可以快速的搭建起运行环境。
不一样之处在于 Testcafe 本来是商业产品,有着成熟的客户群体和解决方案,在转向开源以后依然可以发挥其经验丰富的优点,在一些实现上更注重易用性。所以会封装更高级的指令,例如拖拽、文件上传等等,而且全部指令均用原生 Javascript 实现,因此对浏览器的兼容性很好。还对测试用例提供了预处理器,因此用户能够选择用最新的 Javascript 特性编写用例或者使用 Typescript 这样的超集语言。
高级功能方面 testcafe 支持经过录制操做生成脚本,对于一些非开发人员来讲提供了必定的便利性。
相比之下 Cypress 的目标不只仅是作 selenium 的替代品,更但愿成为革命性的测试框架,所以作了更多大刀阔斧的改进。
以上屡次提到的 debug 问题是 Cypress 的最大优点,Cypress 实现了 DOM 快照、操做回放、指令可视化、网络请求监控等功能让用例执行失败的缘由一目了然。
高级功能方面 Cypress 原生支持截屏、录屏等功能,进一步增强 headless 模式或 CI 模式下的 debug 能力。
Cypress 另外一大高级功能是对应用中的网络请求实现监控、mock 等操做,让一些本来经过 UI 比较难以实现的断言能够转为对网络请求进行判断。
双方的不足更可能是和对方相比较之下产生的。Testcafe 对高级功能的支持有所不足,例如录屏和 DOM 快照等高效的 debug 方案还未实现。Cypress 部分指令和高级功能普通 Javascript 没法实现,须要借助 chrome extension API,所以目前并不能兼容全部的浏览器。而且因为特殊的控制实现方式,Cypress 不能支持跨浏览器、跨 tab 的测试需求,还要求被测试 Web 应用是同源的。此外 Cypress 还有一些临时性的不足,例如用例执行时的负载均衡等高级功能的支持还不完整,一些原生操做例如文件上传也还须要经过 Javascript 模拟,不过这些临时问题都在 roadmap 上排期解决。