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 上排期解決。