iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Software Development

文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念系列 第 22 篇

Day 22:單元測試基礎——測試金字塔理論、斷言(Assertions)與 Mock 機制

  • 分享至 

  • xImage
  •  

昨天把純函式和有副作用的程式碼分開放之後,今天要正式談「測試」這件事本身:測試應該分成幾種層級、每一層各自驗證什麼、以及遇到無法避免的副作用時,該怎麼處理。

一、測試金字塔(Test Pyramid)

定義:測試金字塔把自動化測試分成三層,由下而上分別是:

  1. 單元測試(Unit Test):單獨測試一個函式或類別,不碰真正的資料庫、網路或檔案系統。
  2. 整合測試(Integration Test):測試多個模組串接在一起、或是跟真正的資料庫、外部服務互動時是否正常。
  3. 端對端測試(E2E Test):模擬使用者從頭到尾操作整個系統的流程。

形狀的理由:越上層的測試越慢、越容易因為環境問題而失敗(脆弱),維護成本也越高。理想的比例是「寫大量又快又穩定的單元測試,中等數量的整合測試,少量涵蓋關鍵流程的 E2E 測試」——如果反過來,大量依賴又慢又不穩的 E2E 測試,會讓整個測試套件變得又慢又難維護。

二、斷言(Assertions)

定義:斷言是測試裡「檢查結果是否符合預期」的那一行程式碼——給定輸入、呼叫函式,然後斷言輸出應該等於某個值。如果斷言失敗,測試就會回報錯誤,標示出程式碼跟預期行為不一致的地方。

三、Mock 機制

定義:當測試的對象依賴某個外部服務(資料庫、API)時,為了不讓測試真的去打資料庫或呼叫外部 API,會用一個「假的」替身(Mock / Stub)取代真正的依賴——這個假替身看起來像真的(有同樣的方法可以呼叫),但回傳的是測試預先設定好的固定結果。

四、代購 App 案例

延續 Day 21 的 calculateTotal(純函式)與 createOrder(有副作用)。純函式可以直接寫斷言測試;有副作用的部分,則需要用 Mock 取代真正的 OrderRepository:

// __tests__/calculateTotal.test.js —— 單元測試,純函式不需要任何 Mock
import { calculateTotal } from '../pure/calculateTotal.js';

test('計算訂單總額:商品價格 x 數量 + 運費', () => {
  const result = calculateTotal(100, 2, 30);
  expect(result).toBe(230); // 斷言:輸出必須等於 230
});

// __tests__/orderService.test.js —— 用 Mock 取代真正的 OrderRepository 與 Notifier
test('建立訂單後會呼叫 repository.save 寫入正確的總金額', async () => {
  const mockRepository = { save: jest.fn().mockResolvedValue({ id: 1 }) }; // 假的 repository
  const mockNotifier = { notify: jest.fn() }; // 假的通知服務,不會真的打 LINE API

  await createOrder({ price: 100, quantity: 2, shippingFee: 30 }, mockRepository, mockNotifier);

  expect(mockRepository.save).toHaveBeenCalledWith(
    expect.objectContaining({ total: 230 }) // 斷言:存進去的 total 是否正確
  );
});

calculateTotal 的測試完全不需要 Mock,因為它本來就是純函式;createOrder 的測試則需要把 OrderRepository 和通知服務換成假的實作——這正好呼應 Day 18 談的「依賴介面而非實作」:因為 createOrder 原本就是依賴抽象的 OrderRepository,測試時才能輕易地把它換成一個假的版本,而不需要真的連上資料庫。

五、結論

測試金字塔告訴我們「該在哪一層寫多少測試」,斷言是驗證結果的手段,Mock 則是處理副作用、讓測試能獨立執行的技巧。這三者合起來,才能讓 Day 20 講的「重構後重新跑測試」真正落地,而且因為大部分測試都是又快又穩的單元測試,整個驗證的過程才能又快又可靠。


上一篇
Day 21:可測試性架構(Testability)——純函式(Pure Functions)與無副作用(Side Effects)的價值
下一篇
Day 23:效能瓶頸分析——時間複雜度(Big-O)在前端資料過濾與渲染中的實際體現
系列文
文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言