配置、組織和整合 Cypress 測試的想法#frontend@twiliosendgrid

已發表: 2020-12-15

我們已經在 Twilio SendGrid 為不同​​的 Web 應用程序和前端團隊編寫了許多賽普拉斯測試。 隨著我們的測試擴展到許多功能和頁面,我們偶然發現了一些有用的配置選項並開發了一些方法來更好地維護我們不斷增長的文件數量、頁面元素的選擇器以及沿途的超時值。

我們的目標是向您展示這些關於配置、組織和整合您自己的賽普拉斯相關事物的技巧和想法,因此請隨意使用最適合您和您的團隊的方法。

這篇博文假設您對賽普拉斯測試有一定的應用知識,並且正在尋找更好地維護和改進賽普拉斯測試的想法。 但是,如果您想了解更多關於常見函數、斷言和模式的信息,您可能會發現針對不同環境編寫賽普拉斯測試很有用,您可以查看這篇 1000 英尺概述博客文章。

否則,讓我們繼續並首先向您介紹賽普拉斯的一些配置技巧。

配置你的 cypress.json

您可以在cypress.json文件中設置賽普拉斯測試的所有配置,例如賽普拉斯命令的基本超時、環境變量和其他屬性。

以下是一些提示,供您在配置中嘗試:

  • 微調您的基本命令超時,因此您不必總是在整個賽普拉斯命令中添加{ timeout: timeoutInMs } 修改數字,直到找到“defaultCommandTimeout”、“requestTimeout”和“responseTimeout”等設置的正確平衡。
  • 如果您的測試涉及 iframe,您很可能需要將“chromeWebSecurity”設置為 false,以便您可以在應用程序中訪問跨域 iframe。
  • 嘗試在執行賽普拉斯測試時阻止第三方營銷、分析和日誌記錄腳本,以提高速度而不觸發不需要的事件。 您可以使用其“blacklistHosts”屬性(或賽普拉斯 5.0.0 中的“blockHosts”屬性)輕鬆設置拒絕列表,獲取與第三方主機路徑匹配的字符串 glob 數組。
  • 當您打開賽普拉斯 GUI 時,使用“viewportWidth”和“viewportHeight”調整默認視口尺寸,以使事情更容易看到。
  • 如果 Docker 中存在內存問題以進行繁重的測試,或者您希望幫助提高效率,您可以嘗試將“numTestsKeptInMemory”修改為比默認值更小的數字,並將“videoUploadOnPasses”設置為 false,以專注於為失敗的測試上傳視頻只運行。

另一方面,在調整賽普拉斯配置後,您還可以添加 TypeScript 並更好地鍵入賽普拉斯測試,就像我們在這篇博文中所做的那樣。 這對於調用和鏈接函數(如cy.task('someTaskPluginFunction)Cypress.env('someEnvVariable')cy.customCommand() )時的自動完成、類型警告或錯誤特別有用。

您可能還想嘗試設置基本的cypress.json文件並為每個測試環境加載單獨的賽普拉斯配置文件,例如當您在package.json中運行不同的賽普拉斯腳本時的staging.json 。 賽普拉斯文檔在引導您完成這種方法方面做得很好。

組織 Cypress 文件夾

當您首次在代碼庫中啟動 Cypress 時,Cypress 已經設置了一個具有引導結構的頂級cypress文件夾。 這包括其他文件夾,例如規範文件的integration 、JSON 數據文件的fixturescy.task(...)函數和其他配置的plugins ,以及自定義命令和類型的support文件夾。

在 Cypress 文件夾、React 組件或任何一般代碼中進行組織時,一個好的經驗法則是將可能一起改變的東西放在一起。 在這種情況下,由於我們在瀏覽器中處理 Web 應用程序,因此一種可以很好地擴展的方法是按頁面或總體功能將文件夾中的內容分組。

我們創建了一個單獨的pages文件夾,按功能名稱(例如“SenderAuthentication”)來保存/settings/sender_auth路由下的所有頁面對象,例如“DomainAuthentication”(/settings/sender_auth/domains/**/*)和“LinkBranding” (/settings/sender_auth/links/**/*)。 在我們的plugins文件夾中,我們也做了同樣的事情,將某個功能或頁面的所有cy.task(...)插件文件組織在同一個文件夾中以進行發件人身份驗證,我們對integration文件夾採用相同的方法規範文件。 我們已經將我們的測試擴展到我們的一個代碼庫中的數百個規範文件、頁面對象、插件和其他文件——並且瀏覽起來既簡單又方便。

這是我們如何組織cypress文件夾的概述。

組織integration文件夾(所有規範所在的位置)時要考慮的另一件事是可能根據測試的優先級拆分規範文件。 例如,您可能在“P1”文件夾中擁有所有最高優先級和價值測試,在“P2”文件夾中擁有第二優先級測試,因此您可以通過設置--spec選項輕鬆運行所有“P1”測試,例如--spec 'cypress/integration/P1/**/*'

創建一個適合您的規範文件夾層次結構,以便您可以輕鬆地將規範組合在一起,不僅可以按頁面或功能(例如--spec 'cypress/integration/SomePage/**/*' ,還可以按其他一些標準(例如優先級、產品) ,或環境。

合併元素選擇器

在開發我們頁面的 React 組件時,我們通常會使用 Jest 和 Enzyme 添加一些級別的單元和集成測試。 在功能開發結束時,我們添加了另一層賽普拉斯 E2E 測試,以確保一切都與後端一起工作。 我們的 React 組件的單元測試和 Cypress E2E 測試的頁面對像都需要選擇器來選擇我們希望與之交互和斷言的頁面上的組件/DOM 元素。

當我們更新這些頁面和組件時,可能會出現偏差和錯誤,因為必須將多個位置從單元測試選擇器同步到賽普拉斯頁面對象再到實際組件代碼本身。 如果我們只依賴與樣式相關的類名,那麼記住更新所有可能損壞的地方會很痛苦。 相反,我們將“data-hook”、“data-testid”或任何其他一致命名的“data-*”屬性添加到我們希望記住的頁面上的特定組件和元素,並在我們的測試層中為其編寫選擇器。

我們可以將“數據掛鉤”屬性附加到我們的許多元素中,但我們仍然需要一種方法將它們全部組合在一個地方,以便在其他文件中更新和重用。 我們找到了一種類型化的方法來管理所有這些“數據掛鉤”選擇器,這些選擇器被分散到我們的 React 組件中,並在我們的單元測試和頁面對象選擇器中使用,以便在導出對像中實現更多重用和更容易維護。

對於每個頁面的頂級文件夾,我們將創建一個hooks.ts文件,該文件管理和導出一個對象,該對象具有元素的可讀鍵名和元素的實際字符串 CSS 選擇器作為值。 我們將這些稱為“讀取選擇器”,因為我們需要在單元測試或賽普拉斯的調用中讀取和使用 CSS 選擇器表單來調用元素,例如 Enzyme 的wrapper.find(“[data-hook='selector']”) cy.get(“[data-hook='selector']”)在我們的頁面對像中。 然後這些調用看起來像wrapper.find(Selectors.someElement)cy.get(Selectors.someElement)一樣乾淨。

以下代碼片段詳細介紹了我們在實踐中使用這些讀取選擇器的原因和方式。

類似地,我們還使用元素的可讀鍵名和像{ “data-hook”: “selector” }這樣的對像作為值導出對象。 我們將這些稱為“寫入選擇器”,因為我們需要將這些對像作為道具寫入或傳播到 React 組件上,以便成功地將“數據掛鉤”屬性添加到底層元素。 目標是做這樣的事情下面的實際 DOM 元素——假設 props 被正確地傳遞給 JSX 元素——也將設置“data-hook=”屬性。

以下代碼片段詳細介紹了我們在實踐中使用這些寫選擇器的原因和方式。

我們可以創建對象來整合我們的讀取選擇器和寫入選擇器來更新更少的位置,但是如果我們必須為一些更複雜的頁面編寫許多選擇器怎麼辦? 這可能更容易出錯,因此讓我們創建函數來輕鬆生成這些讀取選擇器和寫入選擇器以最終導出到某個頁面。

對於讀取選擇器生成器函數,我們將遍歷輸入對象的屬性並為每個元素名稱形成[data-hook=”selector”] CSS 選擇器字符串。 如果鍵的對應值為null ,我們將假設輸入對像中的元素名稱將與“data-hook”值相同,例如{ someElement: null } => { someElement: '[data-hook=”someElement”] } . 否則,如果鍵的對應值不為空,我們可以選擇覆蓋該“數據掛鉤”值,例如{ someElement: “newSelector” } => { someElement: '[data-hook=”newSelector”]' }

對於寫入選擇器生成器函數,我們將遍歷輸入對象的屬性並為每個元素名稱形成{ “data-hook”: “selector” }對象。 如果鍵的對應值為null ,我們將假設輸入對像中的元素名稱將與“data-hook”值相同,例如{ someElement: null } => { someElement: { “data-hook”: “someElement” } } 。 否則,如果一個鍵的對應值不是null ,我們可以選擇覆蓋那個“data-hook”值,例如{ someElement: “newSelector” } => { someElement: { “data-hook”: “newSelector” }' } .

使用這兩個生成器函數,我們為頁面構建讀取選擇器和寫入選擇器對象,並將它們導出以在我們的單元測試和賽普拉斯頁面對像中重用。 另一個好處是這些對象的類型是這樣的,我們不會在我們的 TypeScript 文件中意外地執行Selectors.unknownElementWriteSelectors.unknownElement 。 在導出我們的讀取選擇器之前,我們還允許為我們無法控制的第三方組件添加額外的元素和 CSS 選擇器映射。 在某些情況下,我們無法為某些元素添加“data-hook”屬性,因此我們仍然需要通過其他屬性、id 和類進行選擇,並為讀取的選擇器對象添加更多屬性,如下所示。

這種模式可以幫助我們保持對頁面的所有選擇器以及何時需要更新內容的組織。 我們建議您研究在某種對像中或通過任何其他方式管理所有這些選擇器的方法,以最大限度地減少您在未來更改時需要觸及的文件數量。

合併超時

在編寫了許多 Cypress 測試後,我們注意到的一件事是,我們的某些元素的選擇器會超時,並且比cypress.json中設置的默認超時值(例如“defaultCommandTimeout”和“responseTimeout”)花費的時間更長。 我們會回過頭來調整需要更長超時時間的某些頁面,但是隨著時間的推移,任意超時值的數量會增加,並且對於大規模更改而言,維護它變得更加困難。

因此,我們將超時合併到一個從“defaultCommandTimeout”開始的對像中,該對像在 5 到 10 秒的範圍內,以涵蓋我們選擇器的大多數一般超時,例如cy.get(...)cy.contains(...) 。 除了該默認超時,我們將在超時對象內擴展為“short”、“medium”、“long”、“xlong”和“xxlong”,我們可以在文件中的任何位置導入以在 Cypress 命令中使用,例如cy.get(“someElement”, { timeout: timeouts.short })cy.task('pluginName', {}, { timeout: timeouts.xlong }) 。 在用我們導入的對像中的這些值替換我們的超時後,我們有一個地方可以更新以擴大或縮小某些超時所花費的時間。

下面顯示了一個如何輕鬆開始的示例。

包起來

隨著您的賽普拉斯測試套件的增長,您可能會遇到我們在確定如何最好地擴展和維護賽普拉斯測試時遇到的一些相同問題。 您可以選擇根據頁面、功能或其他一些分組約定來組織文件,因此隨著更多開發人員為您的代碼庫做出貢獻,您始終知道在哪裡查找現有測試文件以及在哪裡添加新文件。

隨著 UI 的變化,您可以使用某種類型的對象(例如讀取和寫入選擇器)來維護您的“數據”屬性選擇器,以便您在單元測試和賽普拉斯中斷言或交互的每個頁面的關鍵元素測試。 如果您發現自己開始為賽普拉斯命令的超時值等應用太多任意值,則可能是時候設置一個填充了縮放值的對象,以便您可以在一個地方更新這些值。

隨著前端 UI、後端 API 和 Cypress 測試的變化,您應該始終考慮如何更輕鬆地維護這些測試。 更少的更新和出錯的地方以及更少的不確定性會產生巨大的差異,因為許多開發人員會添加新的頁面、功能和(不可避免地)賽普拉斯測試。

對賽普拉斯的更多帖子感興趣? 看看以下文章:

  • 編寫 E2E 測試時要考慮什麼
  • 編寫柏樹測試的 1,000 英尺概述
  • TypeScript 包含 Cypress 測試中的所有內容
  • 在 Cypress 測試中處理電子郵件流
  • 將 Cypress 測試與 Docker、Buildkite 和 CICD 集成