Node.js Jest 單元測試:打造高品質與高穩定性的程式碼

「如果它沒經過測試,它就是壞的」。在大型專案中,測試是防止退化(Regression)並確保重構安全的唯一防線。Jest 是 Node.js 生態系中最全面且零配置(Zero-config)的測試框架,整合了斷言、Mocking 與代碼覆蓋率等功能。

安裝與環境建立

首先將 Jest 安裝為開發依賴:

npm install --save-dev jest

package.json 中配置測試腳本,以便使用 npm test 啟動:

"scripts": {
  "test": "jest",
  "test:watch": "jest --watchAll"
}

撰寫第一個單元測試

假設你有一個待測函式 utils.js

// utils.js
function calculateTotal(price, quantity) {
  return price * quantity;
}
module.exports = { calculateTotal };

建立對應的測試檔案 utils.test.js

const { calculateTotal } = require('./utils');

// 使用 test 或 it 定義測試案例
test('計算總價:數量 2 * 單價 10 應該等於 20', () => {
  // 斷言 (Assertion)
  expect(calculateTotal(10, 2)).toBe(20);
});

常用的 Matchers (比對器)

Jest 提供豐富的方法來驗證結果是否符合預期:

  • toBe():精確相等(比對基礎型別)。
  • toEqual():內容相等(遞迴比對物件或陣列的內容)。
  • toContain():檢查陣列是否包含特定項。
  • toMatch():使用正規表達式檢查字串。
  • toThrow():檢查函式是否正確拋出異常。

測試非同步程式碼 (Async/Await)

這是 Node.js 開發中最常見的場景。Jest 對 Promise 提供了完美的支援。

test('非同步獲取使用者資料', async () => {
  const data = await fetchUser(1);
  expect(data.name).toBe('Mike');
});

// 或者測試 Promise 的拒絕 (Reject)
test('測試請求失敗', async () => {
  await expect(fetchUser(-1)).rejects.toThrow('User not found');
});

Mocking:模擬外部依賴

有時候我們只想測試邏輯,而不希望真的去讀取資料庫或發送網路請求。這時可以使用 Mock

const db = require('./db');
jest.mock('./db'); // 自動模擬整個模組

test('模擬資料庫查詢', async () => {
  // 強制讓 db.find 回傳我們指定的資料
  db.find.mockResolvedValue([{ id: 1, name: 'Test User' }]);

  const results = await userService.getAll();
  expect(results[0].name).toBe('Test User');
  expect(db.find).toHaveBeenCalled(); // 確認資料庫方法確實有被呼叫
});

代碼覆蓋率 (Code Coverage)

想知道你的測試漏掉了哪些標記嗎?執行:

npx jest --coverage

這會產出一份詳盡的報表,顯示你的程式碼有多少百分比已被測試覆蓋。

總結

  1. Jest 讓撰寫測試變得像寫普通程式一樣直覺。
  2. 善用 Mocking 隔離外部環境,讓你的單元測試跑得又快又穩定。
  3. 把測試當作 「一份活的技術文件」,它會告訴新加入的成員你的程式該如何正確運作。