編寫 E2E 測試時要考慮什麼#frontend@twiliosendgrid

已發表: 2020-09-19

在 Twilio SendGrid,我們在新功能或頁面開發週期結束時編寫端到端 (E2E) 測試,以確保從最終用戶的角度來看,前端和後端之間的所有部分都已連接並正常工作。

我們已經嘗試了各種 E2E 測試框架和庫,例如我們自己的自定義內部 Ruby Selenium 框架、WebdriverIO,主要是 Cypress,如博客文章系列的第一部分第二部分中強調的那樣,記錄了我們在所有領域的遷移解決方案。 無論我們使用什麼框架或庫,我們發現自己都在問同樣的問題,即我們可以自動化哪些功能並為其編寫 E2E 測試。 在確定我們可以測試哪些功能之後,我們還注意到自己一遍又一遍地應用相同的通用策略來編寫和設置測試。

這篇博文不需要任何在某個庫或框架中編寫 E2E 測試的先驗知識,但是如果您已經看過 Web 應用程序並想知道如何在瀏覽器中最好地自動化操作以測試頁面是否正常工作,它會有所幫助。 我們旨在引導您了解如何考慮 E2E 測試,以便您可以將這些問題和編寫測試的一般策略應用於您可能選擇的任何框架。

詢問您是否可以自動化 E2E 測試的問題

在編寫 E2E 測試時,我們需要確保我們在應用程序中測試的頁面中的流程符合某些標準。 讓我們來看看我們問自己的一些高級問題,以確定 E2E 測試是否可以自動化。

1、是否可以通過API等可靠的方式在每次測試前將用戶數據重置回某個狀態? 如果沒有辦法可靠地將用戶重置回您想要的狀態,則它無法自動化,並且無法在部署之前作為阻塞測試的一部分運行。 通過 UI 將用戶返回到某個狀態也是一種反模式,並且通常是不確定的,因為它很慢,並且通過 UI 的自動化步驟已經足夠不穩定了。 無需在瀏覽器中打開頁面即可進行 API 調用以重置用戶狀態更為可靠。 另一種選擇是,如果您有一項服務存在,則在每次測試之前使用適當的數據創建新用戶。 只要我們在每次測試之前重置一個持久用戶或創建一個用戶,我們就可以專注於我們在頁面上測試的部分。

2. 我們是否可以控制我們打算測試的功能、API 或系統? 如果它是您依賴於計費或任何其他功能的第三方服務,有沒有辦法將它們模擬出來或讓它在某些值下確定性地工作? 您希望盡可能多地控制測試以減少片狀。 您可以在每次測試運行時創建具有隔離資源或數據的專用測試用戶,這樣它就不會受到其他任何東西的影響。

3. 服務或功能本身是否足夠一致,可以在合理的超時時間內工作? 通常,您可能必須實施輪詢或等待某些數據被處理並進入數據庫(例如較慢的異步更新和触發的電子郵件事件)。 如果這些服務在合理且可靠的時間窗口內頻繁發生,您可以在等待特定 DOM 元素顯示或數據更新時設置適當的輪詢超時。

4. 我們可以在頁面上選擇我們需要交互的元素嗎? 您是否正在處理您無法控制且無法更改的 iframe 或生成的元素? 為了與頁面上的元素進行交互,您可以添加更具體的選擇器,例如 `data-hook` 或 `data-testid` 屬性,而不是選擇 id 或類名。 id 和類名更容易更改,因為它們通常與樣式相關聯。 想像一下,嘗試從樣式組件或 CSS 模塊中選擇散列的類名或 id。 對於第三方生成的元素或開源組件庫(如 react-select),您可以將這些元素與具有 `data-hook` 屬性的父元素包裝在一起,並選擇下面的子元素。 為了處理 iframe,我們創建了自定義命令來提取我們需要斷言和操作的 DOM 元素,稍後我們將提供一個示例。

需要考慮的因素更多,但歸結為一個問題:我們能否以一致且及時的方式重複此端到端測試並獲得相同的結果?

編寫 E2E 測試的一般策略

1.找出我們可以自動化的高價值測試用例 一些示例包括涵蓋大部分功能流的快樂路徑測試:通過 UI 執行 CRUD 操作以獲取用戶的個人資料信息、過濾表格以匹配給定數據的結果、創建帖子或設置 API 密鑰。 然而,其他邊緣情況和錯誤處理可能更好地包含在單元和集成測試中。 通過我們在上一節中提到的問題運行它,以幫助縮短您的測試用例列表。

2.考慮如何通過API盡可能地設置或拆除來重複這些測試。 對於高價值、可自動化的測試用例,開始注意應該通過 API 設置哪些內容。 如果用戶沒有足夠的可過濾數據進行分頁,如果用戶的數據在 30 天的滾動窗口到期,或者如果我們可能需要刪除一些成功或不完整留下的數據,則一些示例為用戶提供適當的數據在當前測試再次開始之前進行測試。 無論最後一次測試如何成功或失敗,測試都應該能夠運行並將其自身設置為相同的可重複狀態。

重要的是要思考:我如何才能將此用戶的數據重置回起點,以便僅測試我想要的部分功能?

例如,如果您想測試用戶添加帖子以使其最終顯示在用戶的帖子列表中的能力,則必須首先刪除該帖子。

3. 設身處地為客戶著想,跟踪完全完成功能流程所需的 UI 步驟。 記錄客戶完成完整流程或操作的步驟。 在每個步驟之後跟踪用戶應該或不應該看到或與之交互的內容。 我們將在此過程中進行完整性檢查和斷言,以確保用戶遇到正確的事件序列以執行他們的操作。 然後,我們會將健全性檢查轉換為自動命令和斷言。

4. 通過添加特定的選擇器和實現頁面對象(或任何其他類型的包裝器)來維護更改和自動化流程。 查看您寫下的那些步驟,以了解如何操作和完成功能流。 向用戶交互的元素(如按鈕、模式、輸入、表格行、警報和卡片)添加更具體的選擇器,如 `data-hook` 屬性。 如果您願意,您可以創建頁面對象、包裝器或幫助文件,並通過您添加的選擇器引用這些元素。 然後,您可以實現可重用的函數來與頁面的可操作元素進行交互。

5. 使用您創建的助手將您記錄的用戶步驟翻譯成賽普拉斯測試。 在測試中,我們通常通過 API 登錄用戶,並在每個測試用例運行之前保存會話 cookie 以保持登錄狀態。然後我們通過 API 設置或刪除用戶的數據,以獲得一致的起點。 一切就緒後,我們將訪問我們將直接測試我們的功能的頁面。 我們繼續執行流程的步驟,例如創建、更新或刪除流程,斷言在此過程中應該發生什麼或在頁面上可見。 為了加快測試速度並減少不穩定,避免通過 UI 重置或建立狀態,並繞過通過登錄頁面登錄或通過 UI 刪除內容等操作,以專注於您要測試的部分。 確保始終在 `before` 或 `beforeEach` 鉤子中執行這些部分。 否則,如果您使用 `after` 或 `afterEach` 鉤子,測試可能會在兩者之間失敗,導致您的清理步驟永遠不會運行並導致後續測試運行失敗。

6. 錘擊並消除測試薄片。 在實施了測試並在本地通過了幾次之後,很容易設置一個拉取請求,立即合併它,並讓測試與您的測試套件的其餘部分按計劃運行,或者在您的部署步驟中觸發它們。 在你這樣做之前:

    1. 首先,嘗試讓用戶處於各種狀態,看看您的測試是否仍然通過,以確保您有正確的設置步驟。
    2. 接下來,調查在您的一個部署流程期間觸發時並行運行您的測試。 這使您可以查看資源是否被相同的用戶踩踏,以及是否發生了任何競爭條件。
    3. 然後,觀察您的測試如何在 Docker 容器中以無頭模式運行,看看您是否可能需要增加任何超時或調整任何選擇器。

目標是了解您的測試在不同條件下的重複測試運行中的表現如何,並使它們盡可能穩定和一致,以便我們花更少的時間返回修復測試並更多地專注於捕捉環境中的實際錯誤。

這是一個 Cypress 測試樣板佈局示例,我們在其中創建了一個名為“cy.login(用戶名,密碼)”的登錄全局支持命令。我們明確設置 cookie 並將其保存在每個測試用例之前,這樣我們就可以保持登錄狀態並直接進入到我們正在測試的頁面。 我們還通過 API 進行一些設置或拆卸,每次都繞過登錄頁面,如下所示。

結束思想

除了比較哪種 E2E 解決方案最適合使用之外,採用正確的 E2E 測試思維方式也很重要。 首先詢問有關您要測試的功能是否符合自動化要求的問題至關重要。 應該有辦法讓您可靠地將用戶或數據重置回某個狀態(例如通過 API),這樣您就可以專注於您要驗證的內容。

如果沒有可靠的方法將您的用戶或數據重置到正確的起點,您應該考慮構建工具和 API 來創建具有特定配置的用戶。 你也可以考慮模擬出你可以控制的東西,以使測試盡可能穩定和一致。 否則,您應該考慮與您的團隊的價值和權衡。 當推送新的代碼更改時,您應該將此功能留給單元測試或手動回歸測試嗎?

對於可以在 E2E 測試中自動化的那些功能,覆蓋用戶的主要快樂路徑流程通常是最有價值的。 再一次,在您使用您想要的任何框架或庫編寫 E2E 測試時,以及時和一致的方式使事情可重複,並消除脆弱性。

有關具體賽普拉斯 E2E 測試的更多信息,請查看以下資源:

  • 編寫柏樹測試的 1,000 英尺概述
  • TypeScript 包含 Cypress 測試中的所有內容
  • 在 Cypress 測試中處理電子郵件流
  • 配置、組織和整合賽普拉斯測試的想法
  • 將 Cypress 測試與 Docker、Buildkite 和 CICD 集成