E2E 測試之旅第 1 部分:從 STUI 到 WebdriverIO

已發表: 2019-11-21

注意:這是來自#frontend@twiliosendgrid 的帖子。 對於其他工程帖子,請轉到技術博客卷。

隨著 SendGrid 的前端架構在我們的 Web 應用程序中開始成熟,我們希望在我們通常的單元和集成測試層之外添加另一個級別的測試。 我們試圖使用瀏覽器自動化工具構建具有 E2E(端到端)測試覆蓋率的新頁面和功能。

我們希望從客戶的角度自動化測試,並儘可能避免對堆棧的任何部分可能發生的任何重大變化進行手動回歸測試。 我們有並且仍然有以下目標:提供一種方法來為我們的前端應用程序編寫一致的、可調試的、可維護的和有價值的 E2E 自動化測試,並與 CICD(持續集成和持續部署)集成。

我們嘗試了多種技術方法,直到最終確定了用於 E2E 測試的理想解決方案。 在高層次上,這總結了我們的旅程:

  • 構建我們自己的自定義內部 Ruby Selenium 解決方案,SiteTestUI aka STUI
  • 從 STUI 過渡到基於節點的 WebdriverIO
  • 對任何一種設置都不滿意,最終遷移到賽普拉斯

這篇博文是記錄和強調我們的經驗、經驗教訓和權衡使用的每種方法的兩部分之一,希望能指導您和其他開發人員如何將 E2E 測試與有用的模式和測試策略聯繫起來。

第一部分包括我們早期與 STUI 的鬥爭,我們如何遷移到 WebdriverIO,但仍然經歷了許多與 STUI 類似的失敗。 我們將介紹如何使用 WebdriverIO 編寫測試,將測試 Docker 化以在容器中運行,並最終將測試與我們的 CICD 提供商 Buildkite 集成。

如果您想跳到我們今天的 E2E 測試,請繼續閱讀第二部分,因為它完成了我們從 STUI 和 WebdriverIO 到賽普拉斯的最終遷移,以及我們如何在不同團隊之間進行設置。

TLDR:我們在使用 Selenium 包裝器解決方案 STUI 和 WebdriverIO 時經歷了類似的痛苦和掙扎,最終我們開始在 Cypress 中尋找替代方案。 我們學到了很多有見地的課程來解決編寫 E2E 測試以及與 Docker 和 Buildkite 集成的問題。

目錄:

首次涉足 E2E 測試:siteTESUI aka STUI

從 STUI 切換到 WebdriverIO

第 1 步:確定 WebdriverIO 的依賴項

第 2 步:環境配置和腳本

第 3 步:在本地實施 ENE 測試

第 4 步:Docker 化所有測試

第 5 步:與 CICD 集成

與 WebdriverIo 的權衡

搬到賽普拉斯

首次涉足 E2E 測試:SiteTestUI aka STUI

在最初尋找瀏覽器自動化工具時,我們的 SDET(測試中的軟件開發工程師)投入使用 Ruby 和 Selenium 構建我們自己的自定義內部解決方案,特別是 Rspec 和一個名為 Gridium 的自定義 Selenium 框架。 我們重視它的跨瀏覽器支持、為我們的 QA(質量保證)工程師測試用例配置我們自己與 TestRail 的自定義集成的能力,以及為所有前端團隊構建理想存儲庫以在一個位置編寫 E2E 測試並成為按計劃運行。

作為一個渴望第一次使用 SDET 為我們構建的工具編寫一些 E2E 測試的前端開發人員,我們開始為我們已經發布的頁面實施測試,並思考如何正確設置用戶和種子數據以專注於部分我們想要測試的功能。 我們在此過程中學到了一些很棒的東西,比如形成頁面對象來組織幫助功能和我們希望按頁面交互的元素的選擇器,並開始形成遵循這種結構的規範:

我們按照類似的模式逐漸在同一個 repo 中的不同團隊中建立了大量的測試套件,但我們很快就遇到了許多挫折,這將極大地減緩我們對 STUI 的新開發人員和一致貢獻者的進度,例如:

  • 在運行測試套件之前,啟動和運行需要花費大量時間和精力來安裝所有瀏覽器驅動程序、Ruby Gem 依賴項和正確的版本。 我們有時不得不弄清楚為什麼測試會在一個人的機器上運行而不是在另一個人的機器上運行,以及它們的設置有何不同。
  • 測試套件激增並運行了數小時直到完成。 由於所有團隊都為同一個 repo 做出了貢獻,連續運行所有測試意味著要等待幾個小時才能運行整個測試套件,並且多個團隊推送新代碼可能會導致其他地方的另一個測試失敗。
  • 我們對易碎的 CSS 選擇器和復雜的 XPath 選擇器感到沮喪。 下圖充分解釋了使用 XPath 如何使事情變得更複雜,而這些是一些更簡單的事情。

  • 調試測試很痛苦。 我們在調試模糊的錯誤輸出時遇到了麻煩,我們通常不知道事情在哪里以及如何失敗。 我們只能反復運行測試並觀察瀏覽器來推斷它可能失敗的地方以及導致它的代碼。 當 CICD 中的 Docker 環境中的測試失敗時,除了控制台輸出之外沒有太多可看的東西,我們很難在本地重現並解決問題。
  • 我們遇到了Selenium 錯誤和緩慢。 由於所有請求都從服務器發送到瀏覽器,測試運行緩慢,有時我們的測試會在嘗試選擇頁面上的許多元素或在測試運行期間出於未知原因時完全崩潰。
  • 更多的時間花在修復和跳過測試上,而損壞的計劃構建測試運行開始被忽略。 這些測試在實際表示系統中的真實錯誤方面沒有提供價值。
  • 我們的前端團隊感覺與 E2E 測試脫節,因為它存在於與各自 Web 應用程序不同的單獨存儲庫中。 我們經常需要同時打開兩個存儲庫,並在測試運行時繼續在代碼庫和瀏覽器選項卡之間來回查看。
  • 前端團隊不喜歡從每天用 JavaScript 或 TypeScript 編寫代碼到 Ruby 的上下文切換,並且在為 STUI 做出貢獻時必須重新學習如何編寫測試。
  • 由於這是我們很多人在為測試做出貢獻時的第一次嘗試,我們陷入了許多反模式,例如通過 UI 建立登錄狀態,沒有通過 API 進行足夠的拆卸或設置,以及沒有足夠的文檔跟隨什麼使一個偉大的測試。

儘管我們在一個 repo 中為許多不同的團隊編寫了大量的 E2E 測試取得了進展,並學習了一些有用的模式,但我們對整體開發人員體驗感到頭疼,多點故障,以及缺乏有價值的穩定測試驗證我們的整個堆棧。

我們重視一種授權其他前端開發人員和 QA 使用 JavaScript 構建自己的穩定 E2E 測試套件的方法,該套件駐留在他們自己的應用程序代碼中,以促進測試的重用、接近性和所有權。 這促使我們探索 WebdriverIO,這是一個基於 JavaScript 的 Selenium 瀏覽器自動化測試框架,作為我們對 STUI(自定義 Ruby Selenium 內部解決方案)的初始替代品。

我們稍後會經歷它的失敗並最終轉向賽普拉斯(如果 WebdriverIO 的東西不吸引您,請在此處快速前進到第 2 部分),但是我們在每個團隊的存儲庫中建立標準化基礎設施、將 E2E 測試集成到我們前端的 CICD 中獲得了寶貴的經驗團隊,並採用在我們的旅程中值得記錄的技術模式,並讓其他人了解誰可能即將加入 WebdriverIO 或任何其他 E2E 測試解決方案。

從 STUI 切換到 WebdriverIO

在著手開發 WebdriverIO 以減輕我們所經歷的挫敗感時,我們嘗試讓每個前端團隊將使用 Ruby Selenium 方法編寫的現有自動化測試轉換為 JavaScript 或 TypeScript 中的 WebdriverIO 測試,並比較穩定性、速度、開發人員體驗和整體維護測試。

為了實現我們將 E2E 測試駐留在前端團隊的應用程序存儲庫中並在 CICD 和預定管道中運行的理想設置,我們回顧了以下步驟,這些步驟通常適用於任何希望加入具有類似目標的 E2E 測試框架的團隊:

  1. 安裝和選擇依賴項以與測試框架掛鉤
  2. 建立環境配置和腳本命令
  3. 實施針對不同環境在本地通過的端到端測試
  4. Docker 化測試
  5. 將 Dockerized 測試與 CICD 提供程序集成

第 1 步:確定 WebdriverIO 的依賴項

WebdriverIO 為開發人員提供了在許多框架、報告器和服務中進行選擇的靈活性,以啟動測試運行程序。 這需要團隊進行大量的修補和研究才能確定某些庫才能開始。

由於 WebdriverIO 沒有規定使用什麼,它為前端團隊打開了擁有不同庫和配置的大門,儘管整體核心測試在使用 WebdriverIO API 時是一致的。

我們選擇讓每個前端團隊根據自己的喜好進行定制,我們通常使用 Mocha 作為測試框架,Mochawesome 作為報告器,Selenium Standalone 服務和 Typescript 支持。 我們之所以選擇 Mocha 和 Mochawesome,是因為我們的團隊之前對 Mocha 的熟悉和經驗,但其他團隊也決定使用其他替代方案。

第 2 步:環境配置和腳本

在決定了 WebdriverIO 基礎設施之後,我們需要一種方法讓我們的 WebdriverIO 測試在每個環境的不同設置下運行。 這是一個列表,說明了我們希望如何執行這些測試以及為什麼我們希望支持它們的大多數用例:

  • 針對在 localhost 上運行的 Webpack 開發服務器(即 http://localhost:8000),該開發服務器將指向某個環境 API,例如測試或登台(即 https://testing.api.com 或 https:// staging.api.com)。
    為什麼? 有時我們需要對本地 Web 應用程序進行更改,例如為我們的測試添加更多特定的選擇器,以便以更健壯的方式與元素交互,或者我們正在開發新功能並需要調整和驗證現有的自動化測試將針對我們的新代碼更改在本地傳遞。 每當應用程序代碼更改並且我們還沒有推送到已部署的環境時,我們使用此命令針對我們的本地 Web 應用程序運行我們的測試。
  • 針對特定環境(即 https://testing.app.com 或 https://staging.app.com)的已部署應用程序,例如測試或登台
    為什麼? 其他時候應用程序代碼不會改變,但我們可能不得不改變我們的測試代碼來修復一些脆弱性,或者我們有足夠的信心在不進行任何前端更改的情況下完全添加或刪除測試。 我們大量使用此命令來針對已部署的應用程序在本地更新或調試測試,以更接近地模擬我們的測試如何在 CICD 管道中運行。
  • Docker 容器中針對特定環境(如測試或登台)部署的應用程序運行
    為什麼? 這適用於 CICD 管道,因此我們可以觸發 E2E 測試在 Docker 容器中運行,例如針對暫存部署的應用程序,並確保它們在將代碼部署到生產之前或在專用管道中的預定測試運行中通過。 最初設置這些命令時,我們進行了大量試驗和錯誤,以使用不同的環境變量值啟動 Docker 容器,並在將其與我們的 CICD 提供程序 Buildkite 掛鉤之前,測試是否成功執行了正確的測試。

為了實現這一點,我們設置了一個具有共享屬性和許多環境特定文件的通用基礎配置文件,這樣每個環境配置文件都將與基礎文件合併,並根據需要覆蓋或添加屬性以運行。 我們可以為每個環境擁有一個文件,而不需要基本文件,但這會導致常見設置中的大量重複。 我們選擇使用像deepmerge這樣的庫來為我們處理它,但重要的是要注意,對於嵌套對像或數組,合併並不總是完美的。 始終仔細檢查生成的輸出配置,因為當存在未正確合併的重複屬性時,它可能會導致未定義的行為。

我們形成了一個通用的基本配置文件wdio.conf.js ,如下所示:

為了適應我們針對指向環境 API 的本地 webpack 開發服務器運行 E2E 測試的第一個主要用例,我們通過以下方式生成了 localhost 配置文件wdio.localhost.conf.js

請注意,我們合併了基礎文件並將 localhost 特定屬性添加到文件中,以使其更緊湊且更易於維護。 我們還使用 Selenium Standalone 服務來啟動不同類型的瀏覽器,也就是功能。

對於針對已部署的 Web 應用程序運行 E2E 測試的第二個用例,我們設置了測試和暫存應用程序配置文件`wdio.testing.conf.js` 和wdio.staging.conf.js ,類似於以下內容:

在這裡,我們在配置文件中添加了一些額外的環境變量,例如登台專用用戶的登錄憑據,並更新了“baseUrl”以指向已部署的登台應用程序 URL。

對於在 Docker 容器中針對 CICD 提供者範圍內部署的 Web 應用程序運行 E2E 測試的第三個用例,我們設置了 CICD配置文件wdio.cicd.testing.conf.jswdio.cicd.staging.conf.js ,像這樣:

請注意我們不再使用 Selenium Standalone 服務,因為稍後我們將在 Docker Compose 文件中的單獨服務中安裝 Selenium Chrome、Selenium Hub 和應用程序代碼。 該配置還展示了與登台配置相同的環境變量,例如登錄憑據和“baseUrl”,因為我們希望針對已部署的登台應用程序運行我們的測試,唯一的區別是這些測試旨在在 Docker 容器中執行.

建立這些環境配置文件後,我們概述了package.json腳本命令,這些命令將作為我們測試的基礎。 對於這個例子,我們在命令前加上“uitest”來表示使用 WebdriverIO 的 UI 測試,因為我們也用*.uitest.js結束了測試文件。 以下是暫存環境的一些示例命令:

第 3 步:在本地實施 E2E 測試

有了手頭的所有測試命令,我們在 STUI 存儲庫中確定了測試範圍,以便我們轉換為 WebdriverIO 測試。 我們專注於中小型頁面測試,並開始應用頁面對像模式,以有組織的方式封裝每個頁面的所有 UI。

我們可以使用一堆輔助函數或對象文字或任何其他策略來構建結構化文件,但關鍵是要有一種一致的方式來快速交付可維護的測試並堅持下去。 如果特定頁面的 UI 流或 DOM 元素發生變化,我們只需要重構與其相關的頁面對像以及可能的測試代碼即可再次通過測試。

我們通過擁有一個具有共享功能的基礎頁面對象來實現頁面對像模式,所有其他頁面對像都將從該基礎頁面對象擴展而來。 我們有諸如 open 之類的函數來為所有頁面對象提供一致的 API,以便在瀏覽器中“打開”或訪問頁面的 URL。 它類似於這樣:

實現特定頁面對象遵循從基Page類擴展的相同模式,並將選擇器添加到我們希望與之交互或斷言的某些元素以及輔助函數以在頁面上執行操作。

請注意我們如何使用通過super.open(...)打開的基類和頁面的特定路由,因此我們可以通過調用SomePage.open()訪問頁面。 我們還導出了已經初始化的類,因此我們可以引用SomePage.submitButtonSomePage.tableRows等元素,並使用 WebdriverIO 命令與這些元素交互或斷言。 如果頁面對像打算在構造函數中使用其自己的成員屬性進行共享和初始化,我們也可以直接導出類並在測試文件中使用new SomePage(...constructorArgs)實例化頁面對象。

在我們用選擇器和一些輔助功能佈置頁面對象之後,我們編寫了 E2E 測試並通常建模了這個測試公式:

  • 在運行實際測試之前,通過 API 設置或拆除將測試條件重置為預期起點所必需的內容。
  • 登錄到專門的用戶進行測試,這樣每當我們直接訪問頁面時,我們都會保持登錄狀態,而不必通過 UI。 我們創建了一個簡單的login輔助函數,它接收用戶名和密碼,調用我們用於登錄頁面的相同 API,最終返回我們保持登錄所需的身份驗證令牌並傳遞受保護的 API 請求的標頭。 其他公司可能有更多的自定義內部端點或工具來快速創建具有種子數據和配置的全新用戶,但不幸的是,我們沒有足夠充實。 我們會以老式的方式來做,並在我們的環境中通過 UI 使用不同的配置創建專門的測試用戶,並且經常對具有不同用戶的頁面進行測試,以避免資源衝突並在測試並行運行時保持隔離。 我們必須確保專門的測試用戶不會被其他人觸動,否則當有人在不知不覺中修改其中一個時,測試會中斷。
  • 自動執行這些步驟,就好像最終用戶會與功能/頁面進行交互一樣。 通常,我們會訪問包含我們的功能流的頁面,並開始遵循最終用戶的高級步驟,例如填寫輸入、單擊按鈕、等待模式或橫幅出現,以及觀察表格以查看更改的輸出行動的結果。 通過使用我們方便的頁面對象和選擇器,我們快速實現了每個步驟,並且在此過程中進行完整性檢查,我們將斷言用戶在功能流期間應該或不應該在頁面上看到什麼,以確保某些事情的行為符合預期在每個步驟之前和之後。 我們還考慮選擇高價值的快樂路徑測試和有時容易重現的常見錯誤狀態,將其餘較低級別的測試推遲到單元和集成測試。

這是我們 E2E 測試總體佈局的粗略示例(此策略也適用於我們嘗試過的其他測試框架):

附帶說明一下,我們選擇不在本系列博文中介紹 WebdriverIO 和 E2E 最佳實踐的所有技巧和陷阱,但我們將在以後的博文中討論這些主題,敬請期待!

第 4 步:Docker 化所有測試

在雲中的新 AWS 機器上執行每個 Buildkite 管道步驟時,我們不能簡單地調用“npm run uitests:staging”,因為這些機器沒有 Node、瀏覽器、我們的應用程序代碼或任何其他依賴項來實際運行測試.

為了解決這個問題,我們將 Node、Selenium、Chrome 和應用程序代碼等所有依賴項捆綁在一個 Docker 容器中,以便 WebdriverIO 測試能夠成功運行。 我們利用 Docker 和 Docker Compose 來組裝啟動和運行所需的所有服務,這些服務轉換為Dockerfilesdocker-compose.yml文件,並進行了大量實驗,在本地啟動 Docker 容器以使事情正常運行。

為了提供更多背景信息,我們不是 Docker 方面的專家,因此確實需要相當長的時間來了解如何將所有東西放在一起。 Dockerize WebdriverIO 測試有多種方法,我們發現很難將許多不同的服務編排在一起,並篩選不同的 Docker 映像、Compose 版本和教程,直到一切正常。

我們將展示與我們團隊的一個配置相匹配的大部分充實文件,我們希望這為您或任何解決基於 Selenium 的 Dockerizing 測試的一般問題的人提供見解。

在高層次上,我們的測試要求以下內容:

  • Selenium用於執行命令並與瀏覽器通信。 我們使用 Selenium Hub 隨意啟動多個實例,並在 docker-compose 文件中為selenium-hub服務下載了鏡像“selenium/hub”。
  • 要運行的瀏覽器。 我們啟動了 Selenium Chrome 實例並在docker-compose.yml file中為selenium-chrome服務安裝了映像“selenium/node-chrome-debug”。
  • 在安裝了任何其他 Node 模塊的情況下運行我們的測試文件的應用程序代碼。 我們創建了一個新的Dockerfile來為 Node 提供一個環境來安裝 npm 包和運行package.json腳本,複製測試代碼,並在 docker docker-compose.yml文件中分配一個專門用於運行名為uitests的測試文件的服務。

為了提供運行 WebdriverIO 測試所需的所有應用程序和測試代碼的服務,我們製作了一個名為DockerfileDockerfile.uitests並安裝了所有node_modules並將代碼複製到 Node 環境中映像的工作目錄中。 這將被我們的uitests Docker Compose 服務使用,我們通過以下方式實現了Dockerfile設置:

為了讓 Selenium Hub、Chrome 瀏覽器和應用程序測試代碼一起運行 WebdriverIO 測試,我們在 docker docker-compose.uitests.yml文件中概述了selenium-hubselenium-chrom e 和uitest服務:

我們通過環境變量、 depends_on和向服務公開端口來連接 Selenium Hub 和 Chrome 映像。 我們的測試應用程序代碼映像最終會從我們管理的私有 Docker 註冊表中推送和拉取。

我們將在 CICD 期間使用某些環境變量(如VERSIONPIPELINE_SUFFIX )為測試代碼構建 Docker 映像,以通過標籤和更具體的名稱引用映像。 然後我們將啟動 Selenium 服務並通過uitests服務執行命令來執行 WebdriverIO 測試。

當我們構建我們的 Docker Compose 文件時,我們利用docker-compose updocker-compose down等有用的命令以及安裝在我們機器上的 Mac Docker 來本地測試我們的圖像是否具有正確的配置並在與 Buildkite 集成之前順利運行。 我們記錄了構建標記圖像所需的所有命令,將它們推送到註冊表,將它們拉下,並根據環境變量值運行測試。

第 5 步:與 CICD 集成

在我們建立了有效的 Docker 命令並且我們的測試在 Docker 容器中針對不同環境成功運行後,我們開始與我們的 CICD 提供商 Buildkite 集成。

Buildkite 提供了在我們的 AWS 機器上執行.yml文件中的步驟的方法,其中 Bash 腳本和環境變量通過代碼或 Buildkite 設置 UI 為我們的 repo 管道設置。

Buildkite 還允許我們使用導出的環境變量從我們的主部署管道觸發這個測試管道,我們可以將這些測試步驟重用於其他隔離的測試管道,這些測試管道將按計劃運行,供我們的 QA 監控和查看。

在高層次上,我們為 WebdriverIO 和後來的 Cypress 測試 Buildkite 管道共享以下類似步驟:

  • 設置 Docker 映像。 構建、標記測試所需的 Docker 映像並將其推送到註冊表,以便我們可以在後面的步驟中將其拉下。
  • 根據環境變量配置運行測試。 為特定構建拉下標記的 Docker 映像,並針對已部署的環境執行正確的命令,以從設置的環境變量中運行選定的測試套件。

這是一個pipeline.uitests.yml文件的示例,該文件演示了在“構建 UITests Docker 映像”步驟中設置 Docker 映像並在“針對 Chrome 運行 Webdriver 測試”步驟中運行測試:

需要注意的一點是第一步,“構建 UITests Docker 映像”,以及它如何為測試設置 Docker 映像。 它使用 Docker Compose build命令來構建帶有所有應用程序測試代碼的uitests服務,並使用latest${VERSION}環境變量對其進行標記,以便我們最終可以在未來為該構建拉取具有適當標籤的相同圖像步。

每個步驟都可能在 AWS 雲中某處的不同機器上執行,因此標籤唯一地標識特定 Buildkite 運行的映像。 標記圖像後,我們將最新和版本標記的圖像推送到我們的私有 Docker 註冊表以供重用。

在“針對 Chrome 運行 Webdriver 測試”步驟中,我們拉下我們在第一步中構建、標記和推送的映像,並啟動 Selenium Hub、Chrome 和測試服務。 基於諸如$UITESTENV$UITESTSUITE之類的環境變量,我們將選擇要運行的命令類型,例如npm run uitest:以及為此特定 Buildkite 構建運行的測試套件,例如--suite $UITESTSUITE

這些環境變量將通過 Buildkite 管道設置進行設置,或者從 Bash 腳本動態觸發,該腳本將解析 Buildkite 選擇字段以確定要運行哪些測試套件以及針對哪個環境運行。

這是在專用測試管道中觸發的 WebdriverIO 測試的示例,它也重用了相同的pipeline.uitests.yml文件,但在觸發管道的位置設置了環境變量。 此構建失敗並有錯誤屏幕截圖供我們查看,供我們查看Artifacts選項卡和Logs選項卡下的控制台輸出。 記住pipeline.uitests.yml中的artifact_paths (https://gist.github.com/alfredlucero/71032a82f3a72cb2128361c08edbcff2#file-pipeline-uitests-yml-L38),`wdio.conf.js 中`mochawesome`的截圖設置` 文件(https://gist.github.com/alfredlucero/4ee280be0e0674048974520b79dc993a#file-wdio-conf-js-L39),並在 `docker-compose.uitests.yml` 中的 `uitests` 服務中安裝卷(https://gist.github.com/alfredlucero/d2df4533a4a49d5b2f2c4a0eb5590ff8#file-docker-compose-yml-L32)?

我們能夠連接屏幕截圖,以便通過 Buildkite UI 訪問,以便我們直接下載並立即查看,以幫助調試測試,如下所示。

下面顯示了 WebdriverIO 測試的另一個示例,該測試使用pipeline.uitests.yml文件按計劃針對特定頁面在單獨的管道中運行,但已在 Buildkite 管道設置中配置了環境變量。

重要的是要注意,每個 CICD 提供程序都有不同的功能和方法來在合併新代碼時將步驟集成到某種部署過程中,無論是通過具有特定語法、GUI 設置、Bash 腳本或任何其他方式的.yml文件。

當我們從 Jenkins 切換到 Buildkite 時,我們極大地提高了團隊在各自的代碼庫中定義自己的管道、按需跨擴展機器並行化步驟以及使用更易於閱讀的命令的能力。

無論您使用哪個 CICD 提供程序,集成測試的策略在設置 Docker 映像和基於環境變量運行測試方面都是相似的,以實現可移植性和靈活性。

與 WebdriverIO 的權衡

在將大量自定義 Ruby Selenium 解決方案測試轉換為 WebdriverIO 測試並與 Docker 和 Buildkite 集成後,我們在某些​​方面有所改進,但仍然感到與舊系統類似的掙扎,最終導致我們在 Cypress 的下一個也是最後一站我們的端到端測試解決方案。

以下是我們從使用 WebdriverIO 的經驗中發現的與自定義 Ruby Selenium 解決方案相比的一些優點列表:

  • 測試是純粹用 JavaScript 或 TypeScript 而不是 Ruby 編寫的。 這意味著每次編寫 E2E 測試時語言之間的上下文切換和重新學習 Ruby 的時間都會減少。
  • 我們將測試與應用程序代碼放在一起,而不是放在 Ruby 共享存儲庫中。 我們不再覺得依賴於其他團隊的測試失敗,而是更直接地擁有我們回購中功能的 E2E 測試所有權。
  • 我們讚賞跨瀏覽器測試的選項。 借助 WebdriverIO,我們可以針對 Chrome、Firefox 和 IE 等不同功能或瀏覽器進行測試,儘管我們主要專注於針對 Chrome 運行測試,因為超過 80% 的用戶通過 Chrome 訪問了我們的應用程序。
  • 我們接受了與第三方服務集成的可能性。 WebdriverIO 文檔解釋瞭如何與 BrowserStack 和 SauceLabs 等第三方服務集成,以幫助將我們的應用程序覆蓋到所有設備和瀏覽器中。
  • 我們可以靈活地選擇我們自己的測試運行器、報告器和服務。 WebdriverIO 沒有規定使用什麼,因此每個團隊都可以自由決定是否使用 Mocha 和 Chai 或 Jest 等服務。 這也可以解釋為一個缺點,因為團隊開始偏離彼此的設置,並且需要大量時間來試驗我們選擇的每個選項。
  • WebdriverIO API、CLI 和文檔足以編寫測試並與 Docker 和 CIC D 集成。我們可以有許多不同的配置文件,對規范進行分組,通過命令行執行測試,並按照頁面對像模式編寫測試。 但是,文檔可能更清晰,我們不得不挖掘很多奇怪的錯誤。 儘管如此,我們還是能夠從 Ruby Selenium 解決方案轉換我們的測試。

我們在之前的 Ruby Selenium 解決方案中缺乏的很多領域取得了進展,但是我們遇到了很多阻礙我們使用 WebdriverIO 的阻礙,例如:

  • 由於 WebdriverIO 仍然是基於 Selenium 的,我們經歷了很多奇怪的超時、崩潰和錯誤,提醒我們舊的 Ruby Selenium 解決方案的負面閃回。 有時,當我們在頁面上選擇許多元素時,我們的測試會完全崩潰,並且測試會運行得比我們想要的慢。 我們不得不通過很多 Github 問題找出解決方法,或者在編寫測試時避免使用某些方法。
  • 整體的開發者體驗不是最理想的。 該文檔提供了一些命令的高級概述,但沒有足夠的示例來解釋所有使用它的方法。 我們避免使用 Ruby 編寫 E2E 測試,最終使用 JavaScript 或 TypeScript 編寫測試,但 WebdriverIO API 處理起來有點混亂。 一些常見的例子是$$$用於單數與復數元素, $('...').waitForVisible(9000, true)用於等待元素不可見,以及其他不直觀的命令。 我們經歷了很多不穩定的選擇器,不得不為所有東西顯式地$(...).waitForVisible()
  • 對於開發人員和 QA 來說,調試測試非常痛苦和乏味。 Whenever tests failed, we only had screenshots, which would often be blank or not capturing the right moment for us to deduce what went wrong, and vague console error messages that did not point us in the right direction of how to solve the problem and even where the issue occurred. We often had to re-run the tests many times and stare closely at the Chrome browser running the tests to hopefully put things together as to where in the code our tests failed. We used things like browser.debug() but it often did not work or did not provide enough information. We gradually gathered a bunch of console error messages and mapped them to possible solutions over time but it took lots of pain and headache to get there.
  • WebdriverIO tests were tough to set up with Docker . We struggled with trying to incorporate it into Docker as there were many tutorials and ways to do things in articles online, but it was hard to figure out a way that worked in general. Hooking up 2 to 3 services together with all these configurations led to long trial and error experiments and the documentation did not guide us enough in how to do that.
  • Choosing the test runner, reporter, assertions, and services demanded lots of research time upfront . Since WebdriverIO was flexible enough to allow other options, many teams had to spend plenty of time to even have a solid WebdriverIO infrastructure after experimenting with a lot of different choices and each team can have a completely different setup that doesn't transfer over well for shared knowledge and reuse.

To summarize our WebdriverIO and STUI comparison, we analyzed the overall developer experience (related to tools, writing tests, debugging, API, documentation, etc.), test run times, test passing rates, and maintenance as displayed in this table:

Moving On to Cypress

At the end of the day, our WebdriverIO tests were still flaky and tough to maintain. More time was still spent debugging tests in dealing with weird Selenium issues, vague console errors, and somewhat useful screenshots than actually reaping the benefits of seeing tests fail for when the backend or frontend encountered issues.

We appreciated cross-browser testing and implementing tests in JavaScript, but if our tests could not pass consistently without much headache for even Chrome, then it became no longer worth it and we would then simply have a STUI 2.0.

With WebdriverIO we still strayed from the crucial aspect of providing a way to write consistent, debuggable, maintainable, and valuable E2E automation tests for our frontend applications in our original goal. Overall, we learned a lot about integrating with Buildkite and Docker, using page objects, and outlining tests in a structured way that will transfer over to our final solution with Cypress.

If we felt it was necessary to run our tests in multiple browsers and against various third-party services, we could always circle back to having some tests written with WebdriverIO, or if we needed something fully custom, we would revisit the STUI solution.

Ultimately, neither solution met our main goal for E2E tests, so follow us on our journey in how we migrated from STUI and WebdriverIO to Cypress in part 2 of the blog post series.