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