訴諸 Vitest、Storybook 和 Playwright 進行現代化前端測試
本文屬于是語冰的直男翻譯了屬于是,僅供粉絲參考,英文原味版請臨幸 Modern frontend testing with Vitest, Storybook, and Playwright。
向前端工程師提及“測試”,您可能會面臨引發 PTSD(創傷后應激障礙)的風險。就傳統而言,這事倍功半。畢竟,您可以直接在屏幕上看到 UI;為什么需要編寫自動化測試來確認已經在瀏覽器觀察到的內容?
在本文中,我們將分享為什么我們認為前端測試值得一試,為什么它以往風評被害,以及我們采用的使我們的測試易于編寫和維護的方案。
我們為什么要測試前端?
測試我們的 UI 有其他不太明顯的復利。正如 TDD(測試驅動開發)可以鼓勵開發者預先考慮極端用例,UI 組件測試也可以產生相同的效果。
還有,通過使用我們稍后將討論的工具和技術,我們可以確保滿足基本的可訪問性規則。
除此之外,擁有一套成熟的前端測試讓我們有信心快速迭代和更改產品,而不必擔心意外的回歸測試。
那為什么不是每個人都編寫 UI 測試呢?
如果測試前端代碼有這么多好處,為什么經常避免測試呢?不幸的是,時至今日,“成本”方面仍然嚴重曲解了成本效益等式。
其中一個核心問題是,測試通常在 node.js 環境的 CI 中運行,而不是在代碼的實際目標環境 —— 瀏覽器中運行。
開發者不得不使用 jsdom 等工具模擬(偽造)瀏覽器 API,但當測試失敗時,它們想知道是它們的代碼壞掉了,還是只是模擬的行為與真實瀏覽器不同。
此外,在調試失敗的 UI 測試時,測試工具唯一能提供的真正幫助是將一長串 DOM 轉儲到終端中,這可能很難快速解釋。如果這就是你能得到的所有反饋,請您據此弄清楚測試失敗的原因,那我只能祝您好運:
圖片
為了在實際的瀏覽器中測試前端代碼,某些團隊退回到使用端到端測試工具,如 Selenium 或 Cypress,這些工具在真實的瀏覽器中加載網站。雖然這確實提供了更大程度的置信度,但此類測試可能緩慢而脆弱。
它們更適合驗證前端和后端之間的連接,而不是單個組件的行為。
如何編寫前端測試?
幸運的是,由于一大坨優秀的開源開發者的努力,在過去幾年中,測試前端項目變得更加容易和有價值。
本文的其余部分將討論我們使用的四種主要類型的測試以及我們用于創建它們的工具。
單元測試(Vitest)
術語“單元測試”可以表示許多不同的含義。于我們而言,這意味著,對存在于組件之外的可重用邏輯代碼塊進行測試。這些可以是實用程序函數、自定義 React hooks 或最終在組件中導入和使用的任何其他代碼。
對于這些,我們使用 Vitest 作為我們的測試運行程序。我們喜歡它自動獲取我們的 Vite 配置并毫不費力地處理 ESM 依賴項。它在 6.5 秒內對 40 個文件運行 250 多個測試。我們近一半的測試文件還使用 fast-check 進行基于屬性的測試,這意味著,我們可以確信意外輸入不會破壞我們的代碼。
組件測試(Storybook)
我們構建的大部分內容都是 React 組件。這些組件可以是迷你的可復用組件,如按鈕和橫幅,也可以是更復雜的組件,如表格和模態框,甚至是整個頁面。
甚至整個 App 也是由一大坨其他組件合成的組件。因此,于我們而言,制定一個可靠的策略來測試各種組件至關重要,而我們使用的工具是 Storybook。
Storybook 通常被認為是一種文檔工具。許多設計庫都會發布一個 Storybook 網站,列出所提供的組件,提供有關它們的有用提示,并演示如何使用它們。作為一個非常小的團隊,這不是我們使用 Storybook 的主要原因。
取而代之的是,我們將其用作一個隔離的環境,我們可以在 App 之外構建組件,從而在構建它們所屬頁面的其余部分之前為我們提供一個處理它們的地方。
我們為每個組件創建不同的“story”,以反映它可能出現的不同狀態,如錯誤條件、空狀態、默認狀態等——有點像 TDD。
我們甚至可以模擬 API 響應,這樣我們就不用等待后端向我們發送數據,然后再開始開發功能。
不久前,Storybook 還添加了播放功能,該函數能夠觸發與組件的交互,并使用流行的 Testing-Library 工具集斷言它們的行為。有了這個,我們可以像用戶一樣單擊并在組件中輸入文本,然后確保它的行為符合我們的預期。
我們還發現,由于 Testing-Library 鼓勵通過標簽或角色而不是 CSS 類來選擇元素,因此我們的可訪問性也得到了改進。在 CI 中,我們可以使用 Storybook 測試運行程序來執行我們所有的播放功能,并確保沒有任何損壞。
我們目前有 1_000
個故事,涉及 200 多個組件,其中一半以上的 story 具有與斷言的交互測試,這使得這些測試成為我們絕大多數測試,在 CI 中運行大約需要 3 分鐘,分為 6 個作業。
最好的部分是,如果測試失敗,我們可以在瀏覽器中將其拉出并查看出了什么問題,而不是只查看頁面的 HTML 標記。
通過創建 story 和測試從按鈕到整個 App 的所有復雜程度的組件,我們可以確保它們表現正常。
圖片
視覺快照測試(Chromatic)
組件的行為方式確實很重要,但是它的外觀呢?殘缺的樣式不僅會令人尷尬,而且還會導致可用性問題。地球人都知道,用 CSS 編寫的樣式很容易被意外破壞,對任何類型的基本樣式進行更改都可能很可怕。
我剛剛更改了段落的默認邊距,但我是否記得檢查 App 所有塵土飛揚、無人問津的角落的所有極端用例,比如載入和錯誤狀態?
幸運的是,正如您在上面看到的,我們為每個塵土飛揚的角落和極端情況準備了 Storybook story,所以我們可以在創建每個 story 時拍照,然后在發生任何變化時再次拍照,比較它們以尋找差異。
可以建立一個自主開發的系統來做到這一點,并且有許多服務可以按月付費。我們選擇使用 Chromatic,即 Storybook 本身的開發者。
這些視覺快照測試使我們免于多次發布視覺錯誤,并讓我們有信心更改所有標題元素的默認底部間距,而不必擔心我們會讓 App 中的不明頁面看起來殘缺不全。
下面是 Chromatic 在重構 PR 中標記的最近更改的示例,該更改阻止了我們發布殘缺的樣式更改(以綠色顯示):
圖片
端到端測試(Playwright)
最后,在我們的測試堆棧的頂部是端到端(e2e)測試,用于驗證我們部署的 App 是否與部署的數據庫和 API 服務器協同工作。
我們的所有其他測試都使用虛假數據將組件置于特定狀態,并避免發出可能緩慢且不可靠的網絡請求。但是,這意味著,我們需要仔細檢查虛假響應是否與真實服務器的實際行為相匹配。
出于這個原因,我們編寫了一些測試,用于執行從注冊和身份驗證流程(使用真正的魔術鏈接電子郵件和雙因素身份驗證)到主機創建和刪除再到計劃升級的所有內容。
這些不需要測試我們的組件在不同情況下的行為(我們的組件測試已經涵蓋了這一點),而是測試網站如何與后端交互以實現用戶的目標。對于這些測試,我們使用 Playwright,它啟動多個瀏覽器的無頭版本。如果測試在 CI 中失敗,我們將獲得一個調試跟蹤,該跟蹤顯示測試每個步驟的頁面狀態,我們發現這有助于確定測試失敗的原因。
除了每次對前端進行更改時運行這些測試外,我們還會在夜間運行它們,以便在將 API 服務器部署到生產環境之前,在我們的暫存環境中捕獲 API 服務器所做的任何意外的重大更改。
圖片
榮譽獎(靜態分析)
雖然并不總是被認為是“測試”,但還有另一類工具也可以幫助我們避免發布殘缺的代碼。我們使用靜態分析工具,包括 linter(ESLint 和 Stylelint),它們強制執行最佳實踐并幫助避免某些類型的錯誤,我們使用 TS(TypeScript)編寫代碼,它在 JS 上添加了類型支持,讓我們確信我們不會在代碼中輸入拼寫錯誤或調用實際上不存在的方法。