2026 年 API Mock 與測試自動化:MSW (Mock Service Worker) 現代實踐

concept%20visualization%20for%202026%20%E5%B9%B4%2...
發表時間:2026 年 09 月 14 日 | 更新日期:2026 年 09 月 14 日 | 編輯:雅寶社區編輯團隊
2026 年 API Mock 與測試自動化:MSW (Mock Service Worker) 現代實踐|im from './h = ) - 雅寶社區 · 頂客論壇

en)

im from './h from 'vitest'

im from '../src/mocks/server'

before))

from 'msw'

ex,

{ id: ') => {

}),

ex,

{ st

nextCursor: null,

http.get('/api/v1/projects', () => HttpResponse.json(response))

更進一步的做法,是用工具從 OpenAPI 自動產生基本的 handler 骨架,再由人工補上更真實的資料與情境。這樣能省下大量重複的樣板撰寫,同時保留人類判斷「這個端點在真實世界會怎麼回」的空間。要注意的是,自動產生的 handler 只是起點,不應該直接當成最終版本,因為它通常無法表達業務語意與邊界情境。

如果你的專案沒有 OpenAPI,退而求其次的做法是建立一份共用的型別定義檔,並讓前後端都參考它(例如透過 monorepo 的 shared package)。重點不是工具,而是「只有一份來源」。只要你有兩份定義,就一定會漂移。

3-3 讓 Mock 與真實後端「同源」的契約測試

型別一致只解決了「欄位名稱與結構」的問題,但沒有解決「行為」的問題。真實 API 可能會回傳不同的錯誤碼、不同的分頁邏輯、不同的排序規則。這些是型別無法表達的。

契約測試(contract testing)的價值就在這裡。做法是:用同一組測試案例,分別打你的 MSW handler 與真實後端,比對兩者的回應是否符合預期。這可以是輕量的人工驗證,也可以是自動化的 CI 流程。

一個實用的折衷做法是建立「契約快照」:定期(例如每天一次)在 CI 中對測試環境的真實後端發出請求,把回應結構正規化後存成快照,再與 handler 的回應結構比對。這樣不需要每次都打真實後端,又能及早發現漂移。

對於 GraphQL 來說,契約測試的概念類似,但可以透過 schema 驗證與欄位層級的比對來進行。MSW 支援 graphql 命名空間,讓你能針對特定 query 或 mutation 定義回應,這在 schema 較大的專案中特別好用。

這裡要提醒一個常見誤區:不要把「Mock 通過」當成「功能正確」。Mock 永遠是你自己寫的,它只驗證了你對 API 的假設。契約測試的目的,就是定期檢驗這個假設是否還成立。

四、測試自動化中的整合:Unit、Component、E2E

MSW 最實用的特性之一,是同一套 handler 可以跨測試層級重複使用。但在不同層級,設定方式與注意事項略有不同。這一節分別談 Node 測試環境與 E2E 工具的整合。

4-1 在 Vitest/Jest 中的設定要點

Node 測試環境的整合相對單純:建立 server 實例,在測試生命週期中啟動與關閉。前面已經示範過基本設定,這裡補充幾個實務要點。

第一,把 server.listen() 放在全域 setup 檔中執行一次,而不是每個測試檔各自執行。這能避免重複攔截造成的效能損耗,也能確保 resetHandlers() 的行為一致。

第二,afterEach 一定要呼叫 server.resetHandlers()。否則某個測試用 server.use() 加的覆寫會殘留到下一個測試,造成難以追蹤的偶發失敗。這是新手最常踩的坑之一。

第三,測試環境的 onUnhandledRequest 建議設為 'error'。這會讓任何未定義 handler 的請求直接失敗,強迫你補上 handler,而不是讓測試意外依賴真實網路。

第四,如果你使用 MSW 的瀏覽器模式來做元件測試(例如用 Vitest Browser Mode 或 Playwright Component Testing),記得 Service Worker 的啟動是非同步的,且需要在測試環境正確載入 mockServiceWorker.js。這類設定的細節較多,建議在專案初期就建立好共用工具函式,避免每個測試檔各自處理。

第五,善用 delay() 來測試載入狀態,但要控制時間長度。實務上 50 到 200 毫秒通常足夠觸發 loading UI,又不至於拖慢測試。若測試需要更精確控制時間,可以搭配 fake timers,但要注意 MSW 內部的非同步行為與 fake timers 有時會互相影響,需要實際驗證。

4-2 Playwright/Cypress 的整合策略

E2E 測試與 MSW 的關係比較微妙。一方面,E2E 的價值在於驗證真實整合,過度 Mock 會讓 E2E 失去意義;另一方面,某些情境(例如第三方金流、難以重現的錯誤)確實需要 Mock。

在 Playwright 中,原生就有 page.route() 可以攔截請求,因此不一定需要 MSW。但如果你希望 E2E 與單元測試共用同一套 handler,MSW 仍然有價值。做法是在測試環境啟動時注入 MSW 的 Service Worker,讓頁面載入後自動套用 handler。

這裡的關鍵決策是:哪些情境用真實後端、哪些用 Mock。比較穩健的策略是分層,例如:

  • 冒煙測試(Smoke Test):完全不 Mock,打真實測試環境,驗證主要流程能跑通。
  • 邊界情境測試:使用 MSW Mock 特定端點,重現罕見的錯誤或極端資料。
  • 第三方服務:一律 Mock,避免依賴外部服務的穩定性與費用。

    在 Cypress 中,社群過去習慣使用 cy.intercept()。MSW 與 Cypress 的整合需要一些額外設定,因為 Cypress 的執行環境與一般瀏覽器不同。如果你的團隊已經有成熟的 cy.intercept() 用法,不一定需要全面替換;但如果希望降低「同一份 Mock 要維護兩套」的成本,整合 MSW 是值得投資的方向。

    無論用哪個工具,都要記得一個原則:E2E 測試中的 Mock 應該是「例外」而非常態。當你發現 E2E 測試大量依賴 Mock 才能通過,通常代表你的整合測試層(用 MSW 在 Node 環境測試)沒有做好,該補的是中間層,而不是把 Mock 往上搬。

    五、常見陷阱與反模式

    談完做法,來談談失敗。以下這些是在實際專案中反覆出現的反模式,避開它們,你的 MSW 實踐就成功了一半。

    陷阱一:Mock 與真實 API 長期漂移。這是最致命的問題。當 handler 寫完就没人再管,後端改了欄位、改了錯誤格式、改了分頁機制,Mock 依然活在自己的世界。解法是建立契約檢查機制,至少要有定期執行的自動化比對。

    陷阱二:把 Mock 當成規格。有些團隊在 API 還沒設計好時,先寫 handler 當作討論基礎。這本身沒錯,但如果沒有把討論結果回寫到 API 文件,最後就會變成「前後端各自實作自己的版本」。Mock 可以是溝通工具,但不能取代正式規格。

    陷阱三:過度 Mock 導致假陽性。當你把所有端點都 Mock 掉,測試通過只代表「你的元件能處理你設想的回應」,不代表系統能運作。E2E 或整合測試必須保留一部分真實連線。

    陷阱四:Handler 全域註冊造成測試耦合。如果所有 handler 都是全域的,某個測試新增的覆寫可能影響其他測試。解法是使用 resetHandlers(),並盡量讓覆寫範圍局限在單一測試內。

    陷阱五:忽略 mockServiceWorker.js 的版本同步。升級 MSW 套件後忘記重新產生該檔案,會導致瀏覽器端 Mock 完全失效,而且症狀不明顯,容易浪費大量排查時間。建議在 postinstall 腳本中自動執行產生指令。

    陷阱六:把 Mock 資料寫得「太完美」。真實世界的資料有缺漏欄位、有超長字串、有特殊字元、有空陣列。如果 handler 永遠回傳漂亮整齊的資料,UI 就無法被驗證在極端情況下的表現。刻意加入一些「不完美」的資料,是提升測試價值的廉價手段。

    陷阱七:在正式環境誤啟 Mock。這聽起來很蠢,但確實發生過。務必用環境變數或建置條件嚴格控制 Mock 的啟用,並且在 CI 中加入檢查,確保正式建置產物不包含 Mock 邏輯。

    陷阱八:所有人都能改 handler,但沒人負責。當 handler 成為共享資源,就需要明確的維護責任。建議指定一位或一組負責人,並在 PR 流程中要求 handler 變更需要相應的說明。否則它會慢慢腐化成沒人敢動的遺跡。

    六、團隊導入路徑與 2026 年後的展望

    如果你是第一次在團隊導入 MSW,不建議一次到位。比較務實的路徑是分階段進行。

    第一階段:開發環境 Mock。先讓前端在後端未完成時能獨立開發,並且把 handler 集中管理。這個階段的目標是建立「共享的 API 認知」,讓團隊習慣 handler 是共同資產。

    第二階段:單元與元件測試整合。把 setupServer 導入測試環境,讓現有的模組替換式 Mock 逐步汰換。這個階段的重點是教育,讓團隊理解網路層 Mock 與模組替換的差別。

    第三階段:契約驅動。導入 OpenAPI 或 schema 生成型別,讓 handler 有型別保障。再進一步建立契約檢查流程,定期比對真實 API。

    第四階段:E2E 策略整合。明確定義哪些情境使用 Mock、哪些使用真實後端,並把決策寫成團隊規範。

    至於 2026 年之後的走向,可以觀察幾個趨勢。第一,型別驅動的開發會更加普及,Mock 的產生與驗證會更深度地綁定 schema。第二,AI 輔助的測試生成會開始處理「從 OpenAPI 產生高品質 handler 與測試案例」這件事,但短期內仍需要人類定義業務情境。第三,前端與後端的界線會更模糊,像 tRPC 這類端到端型別安全的方案普及後,Mock 的形式也可能隨之改變。

    但不論工具怎麼變,核心問題不會變:如何讓測試驗證的東西盡可能接近真實,同時又保持可控與快速。MSW 在 2026 年的價值,正是它把這個平衡點推到了對開發者更友善的位置。

    結語

    API Mock 從來不是「有就好」的配角。它決定了你的測試是在驗證真實行為,還是在驗證自己想像中的世界。MSW 透過網路層攔截的設計,讓 Mock 變得更容易被信任、更容易跨層級重用,也更容易與真實 API 保持同步——前提是你願意在架構與流程上花心思。

    如果你現在正在使用模組替換式的 Mock,不妨從一個小端點開始,改用 MSW 的 handler 重寫,體會一下差別。如果你已經在用 MSW,那麼可以檢視的是:你的 handler 有型別保障嗎?有契約檢查嗎?情境 handler 有明確命名與管理嗎?這些才是把工具變成能力的關鍵。

    測試自動化的本質,不是追求 100% 覆蓋率,而是讓團隊在改動程式碼時有信心。MSW 在這個目標上提供了一個堅實的基礎,而真正的價值,取決於你怎麼用它。

    🏠 返回首頁