Jest:給你的 React 項(xiàng)目加上單元測(cè)試
大家好,我是前端西瓜哥。
Jest 是一款輕量的 JavaScript 測(cè)試框架,它的賣點(diǎn)是簡(jiǎn)單好用,由 facebook 出品。本文就簡(jiǎn)單講講如何使用 Jest 對(duì) React 組件進(jìn)行測(cè)試。
為什么需要單元測(cè)試?
單元測(cè)試(Unit Testing),指的是對(duì)程序中的模塊(最小單位)進(jìn)行檢查和驗(yàn)證。比如一個(gè)函數(shù)、一個(gè)類、一個(gè)組件,它們都是模塊。
使用單元測(cè)試的優(yōu)點(diǎn):
- 更好地交付高質(zhì)量代碼。代碼不可能沒有 bug,測(cè)試能幫你找出來;
- 更容易重構(gòu)。我們不愿意去重構(gòu)代碼,不去還技術(shù)債,很大原因是測(cè)試覆蓋率不足,害怕遺漏一些邊邊角角的邏輯,導(dǎo)致線上發(fā)生重大事故;
- 可以用測(cè)試描述模塊功能。注釋和文檔容易忘記修改,但測(cè)試用例的描述永遠(yuǎn)是準(zhǔn)確的,因?yàn)椴粚?duì)就無法通過測(cè)試;
- 可測(cè)試性好的代碼,往往可維護(hù)性更好。比如某個(gè)模塊很難測(cè)試,是因?yàn)樗推渌K高度耦合,此時(shí)你需要替換為依賴注入的方式來管理模塊依賴。
Jest 判定測(cè)試腳本
Jest 需要 確認(rèn)哪些是測(cè)試文件,默認(rèn)判斷測(cè)試文件的邏輯是:
- __tests__? 文件夾下的 .js .jsx、.ts 、.tsx 為后綴的文件。
- test.js? 、spec.js 或其他文件后綴 .jsx、.ts 、.tsx。
可以通過設(shè)置 Jest 配置文件的 testMatch 或 testRegex 選項(xiàng)進(jìn)行修改,或者 package.json 下的 "jest" 屬性。
Jest 基本使用
我們先寫一個(gè)簡(jiǎn)單的函數(shù),作為被測(cè)試的模塊。
然后我們用 Jest 來做測(cè)試。
然后執(zhí)行 jest 命令,得到測(cè)試結(jié)果。
test 方法創(chuàng)建了一個(gè)測(cè)試的作用域,該方法有三個(gè)參數(shù):
- 測(cè)試的描述。
- 我們寫測(cè)試代碼的函數(shù)。
- 測(cè)試超時(shí)時(shí)間,默認(rèn)為 5 秒,有些測(cè)試是異步的,我們需要等待。
test 方法有一個(gè)別名叫做 it,二者的功能是一致的,只是語義不同。通常用 test,但在某些情況下更適合用 it。這種情況就是 it 可以和描述語句拼成一句話的時(shí)候,比如:
it 方法和后面的 should be true 拼成了一句主語為 it 的句子,語義更好。
我們通常使用 expect 來測(cè)試一個(gè)模塊的邏輯是否符合預(yù)期。expect 會(huì)將模塊返回的結(jié)果封裝成一個(gè)對(duì)象,然后提供非常豐富的方法做測(cè)試。
比如 toBe 就可以做 Object.is 的對(duì)比測(cè)試。
expect 的實(shí)現(xiàn)思路大致為:
利用了閉包。
還有一些其他的 toXX API,我們稱為 matcher。比如:
- toEqual:對(duì)對(duì)象進(jìn)行深遞歸的 Object.is 對(duì)比。
- toBeTruthy:是否為真值。
- not:對(duì)結(jié)果取反,比如expect(val).not.beBe(otherVal) 表示兩值不相等才通過測(cè)試。
- toContain:數(shù)組中是否含有某個(gè)元素。
- toBeLessThan:是否小于某個(gè)值,可以做性能測(cè)試,執(zhí)行某個(gè)函數(shù)幾千次,時(shí)間不能高于某個(gè)值。
更多 API 可以看文檔:
??https://jestjs.io/docs/expect。??
你可以用 describe 方法將多個(gè)相關(guān)的 test 組合起來,這樣能讓你的測(cè)試用例更好地被組織,測(cè)試報(bào)告輸出也更有條理。
describe 里面可以嵌套 describe,即組里面還可以有組。
異步測(cè)試
如果使用異步測(cè)試,需要將 Promise 作為返回值。
或使用 async / await。
也支持回調(diào)函數(shù)風(fēng)格的測(cè)試,你需要調(diào)用函數(shù)傳入的 done 函數(shù)來表明測(cè)試完成:
生命周期函數(shù)
beforeAll,在當(dāng)前文件的正式開始測(cè)試前執(zhí)行一次,適合做一些每次 test 前都要做的初始化操作,比如數(shù)據(jù)庫的清空以及初始化。
beforeEach,在當(dāng)前文件的每個(gè) test 執(zhí)行前都調(diào)用一次。
afterAll,在當(dāng)前文件所有測(cè)試結(jié)束后執(zhí)行一次,適合做一些收尾工作,比如將數(shù)據(jù)庫清空。
afterEach,在當(dāng)前文件的每個(gè) test 執(zhí)行完后都調(diào)用一次。
React Testing Library
本文不講解安裝和配置,我們先用 CreateReactApp 來搭建項(xiàng)目,并使用 TypeScript 模板。
執(zhí)行單元測(cè)試的命令為:
CreateReactApp 內(nèi)置了 Jest,但 Jest 本身并不支持 React 組件的測(cè)試 API,需要使用另外一個(gè)內(nèi)置的 React Testing Library 庫來測(cè)試 React 組件。
React Testing Library 是 以用戶為角度 的測(cè)試庫,能夠模擬瀏覽器的 DOM,將 React 組件掛載上去后,我們使用其提供的一些模擬用戶操作的 API 進(jìn)行測(cè)試。
React Testing Library 的哲學(xué)是:
測(cè)試的寫法越是接近應(yīng)用被使用的方式,我們就越有自信將其交付給客戶。
CreateReactApp 預(yù)置模板的 App.test.tsx 使用了 React Testing Library。
Enzyme
另一種比較流行的測(cè)試 React 組件的框架是 Enzyme,它的 API 簡(jiǎn)潔優(yōu)雅,能夠用類似 JQuery 的語法,對(duì)開發(fā)非常友好。Enzyme 由 Airbnd 出品,但目前已經(jīng)不怎么維護(hù)了。
為此,你需要裝一些包:
如果你使用了 TS,你還得補(bǔ)上類型聲明。
示例:
目前(2022.10.25) enzyme 官方只支持到 React 16,Enzyme 已死:
使用 Jest 測(cè)試 React 組件
我們先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Button 組件。
然后我們創(chuàng)建一個(gè) button.test.tsx 測(cè)試文件。我們使用 React Testing Library。
我們寫個(gè)測(cè)試。
render 方法會(huì)將 React 組件掛載到虛擬的文檔樹上。screen.debug() 用于調(diào)試,能讓我們看到虛擬樹的完整結(jié)構(gòu)。
測(cè)試 Button 的文本內(nèi)容是否正常顯示:
測(cè)試 Button 的 onClick 能否正常觸發(fā):
測(cè)試 Button 的 className 是否成功添加:
源碼:
??https://github.com/F-star/xigua-ui/blob/main/src/components/button/??tests/button.test.tsx。
執(zhí)行 yarn test :
結(jié)尾
為了讓代碼更健壯,做模塊的單元測(cè)試還是有必要的,Jest 作為流行的測(cè)試庫值得一試。