node.js 的 API 常見 testcase

若要為現有的 Node.js API 撰寫持續整合 (CI) 測試,核心目標是確保每次程式碼變更後,既有的功能不會損壞。這通常從整合測試 (Integration Testing) 入手最有效率。

撰寫 Prompt 的策略

在下 Prompt 給 AI 時,建議直接提供 server.js 的程式碼片段或 API 路由定義。你可以使用類似以下的範例:

我有一個 Node.js 的 server.js 檔案,使用了 Express 框架。請根據這段程式碼,使用 Jest 和 Supertest 撰寫 API 自動化測試腳本。我需要包含正常的請求測試,以及錯誤處理的邊界測試。請直接提供測試程式碼,並告訴我如何在 package.json 設定測試指令。

如果你的 API 有連接資料庫,記得在 Prompt 中補充:請使用 Mock 的方式處理資料庫連線,避免測試過程污染真實數據。

必備的 Test Case 類型

針對 API 伺服器,通常需要涵蓋以下幾類測試案例:

  1. 狀態碼檢查:確保成功的請求回傳 200 或 201,不存在的頁面回傳 404,未授權的存取回傳 401。
  2. 資料結構驗證:檢查 API 回傳的 JSON 格式是否符合預期,欄位名稱與資料型別是否正確。
  3. 邊界條件測試:輸入空的參數、過長的字串或不合法的格式,測試後端是否能正確回傳錯誤訊息而非程式崩潰。
  4. 資料庫互動測試:例如新增一筆資料後,呼叫查詢 API 是否真的能抓到該筆資料。

推薦的測試工具組合

目前 Node.js 社群最主流的組合是 Jest 搭配 Supertest。Jest 是測試執行器,負責執行測試並產出報告;Supertest 則是用來模擬 HTTP 請求,不需要真的啟動伺服器就能測試 API 路徑。

在 CI 流程中(如 GitHub Actions 或 GitLab CI),你會設定一個腳本自動執行 npm test。如果所有測試案例都通過,代表目前的變更是安全的,才能允許合併程式碼。

下一步建議

你可以試著把 server.js 其中一個最重要的 API 路由貼給我,我可以幫你示範如何撰寫對應的測試腳本與 CI 流程設定。


我已經根據您的需求,使用 Jest 與 Supertest 撰寫了完整的 API 自動化測試腳本。為了能在測試環境中獨立運行而不污染真實數據,我對 

server.js 進行了細微調整,並使用了 Mock 技術來模擬資料庫連線。

1. 準備工作:已完成的環境設定

我已經為您安裝了必要的開發依賴:

  • jest: 測試框架
  • supertest: 用於模擬 HTTP 請求的工具
  • jest-environment-node: Node.js 測試環境

並在 

package.json 的 scripts 中新增了測試指令:

json"scripts": {  "test": "NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest"}

NOTE

由於專案使用 ESM (ES Modules),因此必須加上 --experimental-vm-modules 參數。

2. API 測試腳本 (server.test.js)

此測試涵蓋了建言查詢、新增、更新、以及設定值的存取,包含了成功路徑與錯誤處理(如 500 錯誤與 404 回退)。

import request from 'supertest';
import { jest } from '@jest/globals';

// 關鍵:使用 Mock 模擬 better-sqlite3,避免連動真實 database.sqlite
jest.unstable_mockModule('better-sqlite3', () => {
const mStmt = {
run: jest.fn().mockReturnValue({ changes: 1 }),
all: jest.fn().mockReturnValue([]),
get: jest.fn().mockReturnValue({}),
};
const mDb = {
prepare: jest.fn().mockReturnValue(mStmt),
transaction: jest.fn(cb => (...args) => cb(...args)),
exec: jest.fn(),
};
return {
default: jest.fn(() => mDb)
};
});

// 在 Mock 建立後再匯入 app 與 db
const { app, db } = await import('./server.js');

describe('API 整合測試', () => {

beforeEach(() => {
jest.clearAllMocks();
});

describe('GET /api/suggestions (狀態碼與資料結構驗證)', () => {
test('應成功傳回建言列表與 200 狀態碼', async () => {
db.prepare().all
.mockReturnValueOnce([{ id: '2026-001', subject: '測試', status: 'unprocessed', isExtended: 0 }])
.mockReturnValueOnce([]); // 附件

const res = await request(app).get('/api/suggestions');

expect(res.statusCode).toBe(200);
expect(Array.isArray(res.body)).toBe(true);
expect(res.body[0]).toHaveProperty('id', '2026-001');
});

test('當資料庫查詢失敗時應傳回 500', async () => {
db.prepare().all.mockImplementation(() => { throw new Error('DB Error'); });
const res = await request(app).get('/api/suggestions');
expect(res.statusCode).toBe(500);
expect(res.body.error).toBe('DB Error');
});
});

describe('POST /api/suggestions (新增與邊界測試)', () => {
test('成功新增建言應傳回 201', async () => {
const res = await request(app)
.post('/api/suggestions')
.send({
id: 'NEW-01',
subject: '新建議',
content: '內容描述'
});
expect(res.statusCode).toBe(201);
expect(res.body.success).toBe(true);
});
});

describe('PUT /api/settings (資料庫互動與錯誤處理)', () => {
test('應能成功更新設定值', async () => {
const res = await request(app)
.put('/api/settings')
.send({ buildingSpaceDesc: '更新內容' });
expect(res.statusCode).toBe(200);
expect(res.body.success).toBe(true);
// 驗證是否真的呼叫了 SQL
expect(db.prepare).toHaveBeenCalledWith(expect.stringContaining('UPDATE settings'));
});
});

describe('SPA Fallback (404 檢查)', () => {
test('存取不存在的路徑應回傳 index.html 內容', async () => {
const res = await request(app).get('/some-non-existent-page');
expect(res.type).toBe('text/html');
});
});
});

3. 如何執行測試

請在終端機執行以下指令:

npm test

4. 程式碼修改說明

為了支援測試,我對 

server.js 做了以下微調:

  • 導出物件:在檔案末尾加上 export { app, db },讓測試腳本可以存取實體。
  • 防止自動啟動:加上 if (process.env.NODE_ENV !== 'test') 判斷,避免在執行測試時重複啟動 8080 端口。

這個測試方案可以確保您的後端邏輯在修改過程中不會發生

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *