[Day 03] JavaScript 測試框架介紹 & Jest 基本語法
前面講了為什麼要寫測試,再來要來介紹 JavaScript 有哪些常用的測試框架。
目前常用的有以下幾種:
來看一下近五年的下載量:
目前 Jest 是最多人使用的測試框架,也是一開始學測試最容易接觸到的。不過各個框架都有不同的使用時間及優點,簡單來說:
- Jest 是由 Facebook 所開發,在原先的 CRA 中就有內建,所以在 React 的專案中使用率很高。
- Mocha 主要是 Node.js 的測試框架。
- Karma 則是 Angular 官方推薦的測試框架,在 Angular-cli 內建與 Jasmin 做搭配。
- Vitest 是由 Vite 所開發,算是近期崛起的測試框架,跟 Vite 一樣主打快速。
來做一個詳細的比較
種類 | Jest | Mocha | Karma | Vitest |
---|---|---|---|---|
時間 | 2014 | 2011 | 2012 | 2020 |
開發者 | Open Source | Angular | Vite | |
下載量排名 | 1 | 2 | 3 | 4 |
是否內建斷言庫 | ✅ | ❌(需搭配 chi) | ❌(需搭配 Jasmin) | ✅ |
是否含模擬函式 | ✅ | ❌(需搭配 Sinon.js) | ❌(需搭配 Sinon.js) | ✅ |
- 斷言庫:用來判斷測試結果是否符合預期,Ex: expect(1 + 1).toBe(2)
- 模擬函式:用來模擬外部函式,例如 API 請求、時間函式等等。
以 React 來說會比較建議使用 Jest 來當作測試框架,一方面都是 facebook 開發的,整合上會比較合適,另一方面是 Jest 就涵蓋很多測試功能,功能性來說是很完善的,在建置環境上也比較簡單,雖然測試速度沒有 Vitest 快,不過因為歷史比較悠久,所以遇到問題比較容易找到解決方法。
綜合上述,所以接下來的測試範例都會以 Jest 框架為主。
可是我想使用其他框架怎麼辦?
不用擔心~大部分測試框架的語法都差不多,所以學會 Jest 之後,其他框架也可以很快上手。
Jest 特色
- 內建斷言庫
- 內建模擬函式
- 內建覆蓋率報告
- 內建快照測試
- CRA 內建,Next.js、Vite 也支援
安裝 Jest
npm install --save-dev jest
因為 Jest 是在 node 環境底下執行,如果要使用 ESModule 的 import/export 語法,需要安裝 @babel/preset-env 及 babel-jest
npm install --save-dev @babel/preset-env babel-jest
接著在專案根目錄新增一個 babel.config.json 檔案,內容如下:
{
"presets": ["@babel/preset-env"]
}
並在 package.json 中新增 jest transform 的設定
{
"jest": {
"transform": {
"^.+\\.js$": "babel-jest"
}
}
}
斷言庫(基本語法)
假設有一個加總的函式如下:
const sum = (a, b) => {
return a + b;
};
export default sum;
要測試這個函式就會像這樣:
import sum from "./sum";
describe("testing sum function", () => {
it("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3);
});
});
這邊就包含幾個基本的語法
- describe:描述大範圍的測試內容
- test / it:描述詳細的測試案例內容
- expect:斷言函式
- toBe:斷言函式的方 法
測試時在 Terminal 下指令
npx jest sum.test.js
Jest 就會去跑測試案例,顯示是否通過測試
模擬函式
在寫測試時,要盡量把測試的項目單一化,所以如果函式裡有借助外部的模組或函式,就會需要使用模擬函式的方式,方便我們進行測試。 比較常見的例子像是使用 axios 去發 API 請求,這時候就會需要模擬 axios 的函式,讓測試更加單純。
這邊我去抓 jsonplaceholder API 的使用者名稱,然後把資料經 過處理回傳出來。
import axios from "axios";
const fetchData = async () => {
// 使用 axios.get
const data = await axios.get("https://jsonplaceholder.typicode.com/users/1");
const content = `My name is ${data.username}`;
return content;
};
export default fetchData;
當我要測試這個函式時,就需要去模擬 axios 的回傳資料,這邊使用 jest.mock
去模擬 axios,再使用 mockResolvedValue()
方法去模擬回傳的資料。
import axios from "axios";
import fetchData from "./fetchData";
jest.mock("axios"); // 模擬 axios
describe("testing fetchData function", () => {
it("fetchData", async () => {
axios.get.mockResolvedValue({ username: "Bret" }); // 模擬 axios.get 回傳
const data = await fetchData();
expect(data).toBe("My name is Bret");
});
});
這樣就可以專注在測試 fetchData 函式本身,也不會真的發 API 出去。
測試覆蓋率
Jest 有提供一個測試覆蓋率的功能,可以顯示有多少的程式碼有被測試到,只要下指令
jest --coverage
就會在 Terminal 顯示出各個檔案的測試覆蓋率
不過這樣其實很不好看,所以 Jest 也提供了網頁版的測試覆蓋率報告,在下指令的同時,會建立一個 coverage 的檔案夾,lcov-report 裡面會有一個 index.html,打開檔案就可以看到網頁版的測試覆蓋率報告。
測試覆蓋率有四個指標:
- Statements:有多少比例的語句被測試到,一個 console.log 就算一個語句,一行程式碼可以有多個語句。
- Branches:條件語句,例如 if、else、switch 等等
- Functions:函式有被執行到的比例
- Lines:程式碼有被執行到的行數量,數量通常會比 Statements 少。