React + TypeScript + Jest + React Testing Library + MSW 前端測試工具安裝與配置
我目前所在的開發團隊一直以來都沒有寫測試的習慣,所有的前端專案都沒有任何測試文檔,也沒有配置測試環境。剛進入公司時,一直都很想嘗試為公司的專案導入前端測試,但一方面對公司的 codebase 和開發流程不夠熟悉,另一方面後來又持續收到許多排成很趕的工項,一直沒時間去研究。最近趁著手上的工項都告一段落,決定拿手上其中一個 codebase 比較乾淨的專案來嘗試為公司導入前端測試,以提供未來其他專案導入測試時一個參考範本。
這篇文章主要記錄近期在工作時嘗試在手上的其中一個專案中安裝與配置的測試工具,我在這個專案中選擇使用了 Jest + React Testing Library + MSW
這幾個測試工具,並使用 Husky + lint-staged
設定在 pre-commit 時自動執行相關測試文件的自動測試。除了本文章所介紹的測試工具以外,React 生態系還有許多受歡迎的測試工具,像是近期有取代 Jest 勢頭的 Vitest,以及用來做 E2E 測試的 Cypress 等,都很值得嘗試與研究。
套件安裝
前端基本開發環境
-
React 18
-
TypeScript
-
Webpack
-
Babel
// .babelrc 設定
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
Jest 相關套件
yarn add --dev jest babel-jest jest-environment-jsdom jest-svg-transformer identity-obj-proxy
React Testing Library 相關套件
yarn add --dev @testing-library/react @testing-library/jest-dom @testing-library/user-event @testing-library/dom
Mock Service Worker 相關套件
yarn add --dev msw@latest dotenv undici@5
Jest 相關套件介紹與配置
Jest
Jest
是一個由 Facebook 開發的開源 JavaScript 測試框架,它提供了完整的單元測試環境,包括斷言、模擬、測試監視和報告等功能。Jest 使用 describe
和 it
塊來組織測試。describe
塊用於描述測試的大組別,而 it
塊用於描述單個測試。每個測試都應該包含一個或多個斷言,用於驗證測試的結果。
jest.config.js 配置文檔
module.exports = {
collectCoverage: true, // 啟用覆蓋率收集
collectCoverageFrom: [ // 指定收集覆蓋率的檔案範圍
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
],
coverageDirectory: 'coverage',
};
package.json 配置文檔
"scripts": {
"test": "jest --verbose",
"test:coverage": "yarn test --coverage",
"test:watch": "yarn test --watch",
"test:watch:coverage": "yarn test --watch --coverage",
},
1. Statements : 有多少比例語句被執行到,一個 console.log(); 就算是一個語句,一行中可以有多個 Statements。
2. Branches:條件語句,像是 if ... else 或是 switch,每個情況都是一個 Branch。
3. Functions:一個檔案有多少比例的函式被執行到。
4. Lines:有幾行的程式碼被執行到,基本上 Lines 的數量會小於等於 Statements 的數量
安裝 jest 後我在 src/test/ 目錄下建立了一個簡單的 sum.ts 和 sum.test.ts 來測試,並在 package.json 中設置了:
"scripts": {
"test": "jest",
},
當我下 yarn test 指令後卻出現以下錯誤訊息
Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/strip-ansi/index.js from /Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/string-width/index.js not supported.
Instead change the require of /Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/strip-ansi/index.js in /Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/string-width/index.js to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/string-width/index.js:2:19)
at Object.<anonymous> (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/cliui/build/index.cjs:291:21)
at Object.<anonymous> (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/yargs/build/index.cjs:1:60678)
at Object.<anonymous> (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/yargs/index.cjs:5:30)
at _yargs (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/jest-cli/build/run.js:30:39)
at buildArgv (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/jest-cli/build/run.js:149:26)
at Object.run (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/jest-cli/build/run.js:124:24)
at Object.<anonymous> (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/jest-cli/bin/jest.js:16:17)
at Object.<anonymous> (/Users/boshkuo/Desktop/D8AI/ctbc-tc-test/frontend/node_modules/jest/bin/jest.js:12:3)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
神奇的是如果我直接執行 npx jest 是不會跳出這個錯誤的。看起來這個錯誤應該跟 yarn 脫不了關係
查了一下發現 github 社群上有很多人遇到相同的問題:
- [Bug?]: Error [ERR_REQUIRE_ESM]: require() of ES Module string-width/index.js #8994
- [Bug]: string-width dependency stops storybook from executing
參考社群上的解法, 把 yarn.lock 刪除後重新 yarn 一次就沒有報錯了
babel-jest
由於 Jest 原生支持 CommonJS 模塊,當使用 ES6、TypeScript 或其他編譯語言時,我們需要 babel-jest
來轉換這些語法。
對應配置
// jest.config.js
module.exports = {
transform: {
'^.+\\.(ts|tsx|js|jsx)$': 'babel-jest', // 使用 babel-jest 轉換 ts, tsx, js, jsx 文件
},
};
注意事項:
- 實際測試發現,沒有裝也沒有發生錯誤,這是因為目前開發環境使用的 Node.js V20 以支援 ES6 語法,因此在本專案可以不裝。
- 若同時使用
ts-jest
,建議配置中設定 transform 的檔案類型不要包括 .ts, .tsx 檔,原因參考ts-jest
章節
ts-jest
雖然 Jest 支援透過 Babel (babel-jest) 編譯 TypeScript 測試檔,但畢竟 Babel 僅純粹負責將 TS 轉成 JS,因此執行測試時 Jest 並不會檢查測到試程式碼內的型別錯誤。ts-jest
是一個讓 Jest 能夠測試 TypeScript 程式碼 的 Jest 插件,他不僅能讓 Jest 能夠轉譯 TypeScript 程式碼,還會在執行測試之前先對 TypeScript 程式碼進行靜態類型檢查,幫助發現潛在的型別錯誤。
當執行測試時,檢查到測試檔案的型別錯誤,會顯示如下錯誤訊息,並且在該行終止該測試檔案的測試任務,不繼續往下執行該測試檔案中的其他測試。
src/test/sum.test.ts:9:17 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.