Jest | 讓 Jest 為你的 Code 做測試-基礎用法教學
前言
單元測試是進入前端工程後一直很想學的技能,主要是做過測試能讓自己的程式碼維持一定的水準,尤其在團隊開發時,更不會在上線時因為沒注意到某個細節而產生 Bug ,導致專案出現問題。
單元測試
單元測試是指為專案中每個單一行為做測試,通常專案裡的最小單位都是一個 function
,當每個 function
經過測試,確保邏輯是正確的,那當他在專案裡運行時也就比較不會發生問題,且留下的測試文件也可以在團隊討論或交接時更清楚。
Jest
用於前端的測試框架不是只有 Jest
,選擇原因是因為筆者較擅長使用 React
,而 Jest
在測試方面和 React
的整合度較佳。除了 Jest
以外,常聽到的測試框架還有 mocha
。
建立專案
首先到專案資料夾的目錄下,在 node.js
環境下創建 npm
專案:
npm init -y
這時候在專案資料夾內會產生記錄著專案開發設定的 package.json
,如果還不曉得如何安裝 node.js
可以參考「第一次建置node.js開發環境和安裝npm就上手!」的說明。
下載套件
透過 npm
套件管理工具下載 Jest
測試框架到專案的執行環境中:
npm install jest --save-dev
設定測試指令
打開 package.json
,可以看到剛剛下載的 Jest
已經被記錄在 devDependencies
中了:
在上圖第一行的 scripts
是在 npm
的開發環境中設定執行指令的地方,上方的 test
便是預設的指令之一,可以直接把他的內容改掉,如下:
scripts: {
"test": "jest"
}
設定好後,只需在終端機中使用以下方式輸入,便能執行設定好的對應指令:
npm run test //對應指令
需要注意的是,如果是使用 npm
的 scripts
執行指令,那他的執行環境就是專案本身,尋找執行環境中的 Jest
或其他套件執行。
換個說法,直接在終端機中輸入 jest
,執行環境就不會在專案內,而是以全域為主,但是全域環境下並沒有安裝 Jest
便會出錯,除非將 Jest
安裝在全域中:
npm install jest -g // -g 代表全域
建立測試
Jest
在執行測試時,會尋找專案中副檔名為 .test.js
結尾的檔案,但不限制要放在哪個資料夾,所以在根目錄新增一個 index.test.js
建立第一個測試:
test('Check the result of 5 + ', () => {
expect(5 + 2).toBe(7)
})
把上方的 test
當作一個函式,負責描寫一個單元測試,他擁有兩個參數:
- 第一個參數為「測試名稱」,能夠簡單描述這部分是在測試什麼邏輯或功能。
- 第二個參數是一個函式,又稱斷言,函式內的
expect
用來描述被測試的內容,toBe
是測試內容的回傳值是否符合期望值,例如上方的測試內容為「5加上2期望會等於7」。
執行測試
在終端機中輸入在 scripts
中設定好的指令:
npm run test
執行後會顯示測試的結果:
結果內會顯示 Jest
測試了哪些 .test.js
檔案,還有每個測試( expect
)內的結果( toBe
)是否正確符合,符合的話會輸出 PASS。
現在把 toBe
內的數字改成 8 ,再進行一次測試:
當測試失敗時, Jest
會再結果中顯示哪個檔案內的測試有問題,並會提示正確的結果 Received
。
這就是使用 Jest
測試的基本方法,但實際上需要測試的函式都已經寫好了,並不會在 expect
中寫下邏輯,因此可以直接在 expect
中呼叫函式,測試結果的正確性,例如:
//要測試的函式
const sum = (a, b) => {
return a + b
}
test('Check the result of 5 + 2', () => {
//在 expect 中呼叫函式測試結果
expect(sum(5, 2)).toBe(7)
})
其他斷言
斷言的種類有很多,上方的 toBe
只是其中一種測試方式,除此之外還有以下的斷言可以使用:
除了 toBe
外,對字串還可以用 toMatch
搭配正規表達式檢查:
test('Use toMatch test',()=>{
//搭配正規表達式
expect('Happy new year.').toMatch(/new/)
})
確認物件是否等於期望值需使用 toEqual
:
test('Check the object type', () => {
let peopleA = {
name: 'GQSM'
}
peopleA.age = 25
//測試字串
expect(peopleA.name).toBe('GQSM')
//測試物件
expect(peopleA).toEqual({ name: 'GQSM', age: 25 })
})
預防函式回傳某個結果可以使用 not
:
test('Use not', () => {
let peopleA = {
name: 'GQSM'
}
//確認 name 不等於空
expect(peopleA.name).not.toBe('')
peopleA.name = ''
//如果 name 是空的
expect(peopleA.name).not.toBe('')
})
上方的第 9 行 peopleA.name
是空的,而在使用 not
的情況下結果需不等於期望值,所以不會通過測試:
確認數字結果的斷言:
test('test integer', () => {
//整數可以使用 toBe 或 toEqual 斷言
expect(5).toBe(5)
expect(5).toEqual(5)
//測試輸出值是否大於期望值
expect(5).toBeGreaterThan(4)
//測試輸出值是否大於等於期望值
expect(5).toBeGreaterThanOrEqual(5)
//測試輸出值是否小於期望值
expect(5).toBeLessThan(6)
//測試輸出值是否小於期望值
expect(5).toBeLessThanOrEqual(5)
})
需要注意在 JavaScript
中的小數點運算會產生誤差,因此浮點數需要使用 toBeCloseTo
做斷言,他會捨棄掉些微的誤差:
test('Test float', () => {
//會忽略些微的誤差
expect(0.1 + 0.2).toBeCloseTo(0.3)
//需完全相等
expect(0.1 + 0.2).toBe(0.3)
})
上方使用 toBe
會不通過測試,因為 toBe
需要完全符合:
對陣列可以用 toContain
判斷陣列內是否含有某值,或搭配迴圈對每個位置做斷言:
test('For array test in jest',()=>{
let arrA = ['A','B','C']
//檢查陣列內是否含有某值
expect(arrA).toContain('B')
//搭配迴圈檢查每個位置都不等於空
for(let i in arrA){
expect(arrA[i]).not.toBe('')
}
})
最後是用來判斷特殊值的斷言,例如 undefined
、 null
、 true
等等:
test('Special value',()=>{
//期望值為 true
expect(true).toBeTruthy()
//期望值為 false
expect(false).toBeFalsy()
//期望值為 null
expect(null).toBeNull()
//期望值為 undefined
expect(undefined).toBeUndefined()
//期望值為 undefined 之外的值
expect(null).toBeDefined()
})
產生覆蓋率報告
在測試的時候,會使用各種斷言來確認結果是否與期望值符合,但是如果測試的內容遺漏了某個條件分支,便無法確認該分支的邏輯性是否正確。
覆蓋率就是用來統計被測試的函式,程式碼的執行比例,當函式內所有程式都被測試過,那覆蓋率就會呈現 100% 。
另外, Jest
內建的覆蓋率會以每個 JavaScript
檔案統計,因此在產生前還需要將「函式」與「測試」檔案分開,如下:
建立 ./funcs/func.js
放要測試的函式:
const sum = (a, b) => {
if (b)
return a + b
else
return a
}
module.exports = {
sum: sum
}
func.js
中的 sum
有兩個分支,一個是在 b
有值的時候回傳 a + b
,另一個是在 b
沒有值的情況下直接回傳 a
,最後使用 module.exports
將 sum
匯出。
接著建立測試檔案 ./test/index.test.js
:
let func = require('../funcs/func.js')
test('test sum',()=>{
expect(func.sum(2)).toBe(2)
})
最後到 package.json
中在 test
的指令後加上 —coverage
,讓 Jest
執行完時同步產生測試報告,當然也可以另外設定新指令:
執行測試後,除了測試結果外,還會產生覆蓋率統計資訊:
也可以發現在專案目錄下多了一個叫做 converage
的資料夾,裡面的 Icov-report
內有個 index.html
,打開後也可以看到相同的資訊:
透過點擊 func.js
可以確認更詳細的測試過程,包含是哪一行沒有執行,或每行各執行了幾次:
現在為 ./funcs/func.js
內的 sum
增加一個斷言句,讓 Jest
在測試時進入 sum
的另一個分支:
let func = require('../funcs/func.js')
test('test sum', () => {
expect(func.sum(2)).toBe(2)
//增加斷言
expect(func.sum(2, 3)).toBe(5)
})
執行測試後再一次點開 converage
內的 html
測試報告:
func.js
的詳細執行資訊,在判斷 b
是否有值的地方執行了兩次,且已經沒有還未覆蓋的區塊:
本文解釋了基本的 Jest
測試,並簡單描述斷言庫和產生覆蓋率報告,今後的文章會繼續解釋 Jest
提供的其他功能,一直到用於測試 React
的 Component
組件。
如果文章中有任何問題,或是不理解的地方,都歡迎留言告訴我,謝謝大家!
參考文章