與其他工具的比較

以下是 Mirage 與其他 API 模擬工具和方法的比較。

JSON Server

JSON Server 是一個用於在 node 中建立即時 REST API 的熱門工具。

Mirage 和 JSON Server 之間的主要區別在於,Mirage 的設計目的是模擬您產品 API 的確切形狀,而 JSON Server 則根據其慣例和您在資料庫檔案中放入的內容建立特定的 API 格式。雖然有一些配置選項可用,但使用 JSON Server 完全複製您的生產 API 路由和有效負載可能會很困難或不可能,這意味著您的應用程式程式碼將不會針對類似生產的環境編寫和測試。

Mirage 的理念是停留在 HTTP 邊界,不影響您的應用程式程式碼。您的生產 API 是驅動 Mirage 配置的因素,而不是反之。

Mirage 的其他一些優勢

  • Mirage 與您的前端程式碼一起在瀏覽器中運行,因此在您的開發環境中運行它並使用它在您的 CI 伺服器上運行測試更簡單 – 無需額外處理或重置的流程。

  • Mirage 通過其 ORM 支援完整的參考完整性,並在您的應用程式修改資料時管理您所有資源的外鍵。

  • Mirage 會在每次瀏覽器重新載入時重置其狀態,這可能是一把雙面刃,但通常是您在開發和測試時想要的。(如果您想為 Mirage 添加臨時持久性,可以使用 localStorage。)

  • Mirage 可與 GraphQL 或任何其他 API 格式一起使用。

JSON Server 相較於 Mirage 確實有一些優勢

  • 它是一個真正的伺服器,因此您可以使用開發工具中的網路索引標籤和其他 HTTP 工具(例如 Paw 或 curl)來傳送請求並檢查回應。

  • 它以 node 和 express 編寫,因此您可以獲得 node 生態系統的全部功能。

  • 它具有現成的篩選和排序實作(儘管同樣,它們不太可能與您的生產 API 相符)。

MSW

MSW 是一個可以在瀏覽器或 Node 環境中運行的模擬(mocking)函式庫。

雖然 Mirage 和 MSW 都是在客戶端與你的前端程式碼一起運行,但主要區別在於 MSW 使用 Service Worker 來攔截網路請求,而 Mirage 則使用 Pretender.js,其運作方式是透過 monkey-patching window.fetchwindow.XMLHttpRequest

使用 Service Worker 的主要優點是,請求會顯示在 DevTools 的「網路」標籤中 – 從瀏覽器的角度來看,它們是真實的 HTTP 請求。這使得檢查來自 MSW 的回應的開發體驗更接近與真實伺服器端 API 互動的體驗。

MSW 的其他一些優點:

  • 它可以在 Node 環境中運行,這意味著你的模擬可以在非瀏覽器的 Node 環境中共享。Mirage 也可以在 Node 環境中運行,但僅限於類似瀏覽器的環境(例如,在存在像 jsdom 這樣的東西時,Jest 之類的工具會為你配置)。目前,Mirage 無法用於模擬 React 應用程式的伺服器端 API 請求。

  • 它具有第一方 API 來幫助你模擬 GraphQL。(Mirage 也可以用來模擬 GraphQL,只是目前在該函式庫中沒有第一方 API 可以幫助你做到這一點。)

Mirage 相較於 MSW 的主要優勢與它相較於其他僅提供底層攔截器功能的函式庫的優勢相似。MSW 的主要 API 是一個路由處理器,它給你一個請求,並讓你編寫一個回應。Mirage 提供類似的路由處理器 API,可以以完全相同的方式使用,但它還帶有一個資料庫、對關係的支持、使創建不同資料場景變得容易的工廠、一個序列化層來幫助你將 JSON payload 的形狀與你的生產 API 相匹配,以及一個伺服器實例,使得在你的測試中斷言資料庫狀態和變更變得容易。

在連接任何攔截器(Mirage、MSW、Pretender、fetch-mock 等)之後,以忠實再現你的生產 API 行為的方式實作你的 API 路由通常是你模擬程式碼中最複雜的部分。因此,任何僅提供攔截器功能的函式庫都會將這項工作留給你。對於 GraphQL 和 REST API 都是如此。

Mirage 的其他一些優勢

  • Mirage 的設定稍微容易一些 – 只需要在任何前端程式碼旁邊導入即可。

    import { createServer } from "miragejs"
    
    let server = createServer()
    server.get("/api/movies", () => ["Interstellar", "Inception"])

    MSW 需要額外的步驟 – 你需要在專案的 public 目錄中產生一個 Service Worker 檔案,你可能還希望在生產版本中將其移除。

  • Mirage 是為測試而構建的,並帶來了開發與測試資料種子等概念,以及在你的測試中直接建立不同資料場景的能力,而無需重寫路由處理器。

MSW 是一個很棒的函式庫,而且 Service Worker 的概念甚至可能在某個時候被引入 Mirage,但手動模擬每個端點既繁瑣又難以長期維護。對於你的模擬有一些約定,並確保它們在你的 JavaScript 應用程式發出讀寫請求時保持資料的參照完整性,是Mirage 最初被創建的原因

其他底層模擬函式庫

有一些底層的模擬工具,例如 PretenderFakeRest,它們通常單獨使用(通常在測試中)來為特定端點定義靜態模擬。(Mirage 實際上在底層使用 Pretender 作為其攔截器!)

這些工具與 Mirage 之間最大的區別在於,Mirage 的設計目的是讓你忠實地重現整個生產 API 伺服器,以便你的應用程式可以像在生產環境中與網路對話一樣與其互動。Mirage 的資料和序列化層是實現此目的的原因。手動模擬每個端點可能很繁瑣且難以保持更新,並且是Mirage 最初存在的真正原因

GraphQL 查詢模擬

在 GraphQL 中工作時,通常在查詢層級進行模擬,使用像 Apollo MockedProvider 這樣的套件。這與底層 REST 模擬函式庫有類似的問題,並且在模擬多個查詢時(例如,你的應用程式建立一個 movie,然後接著查詢 allMovies,但新的電影不存在。)很難保持資料的參照完整性。

這些相同的痛點也是導致 Mirage 創建的原因,Mirage 根本上是在資源層級而不是查詢層級上運作。

Cypress 路由模擬

Cypress 包括 cy.route() API,它允許你在測試期間替換你的後端。

Cypress 的網路模擬基於靜態 fixture(通常是 JSON 檔案),因此與其他不使用類似資料庫的東西來確保 CRUD 操作中資源一致性的工具存在相同的問題。

Cypress 還有一個 alias API,它鼓勵你在測試中明確等待其替換。

cy.get("#autocomplete").type("Book")

// Wait for the request + response
cy.wait("@getSearch")

cy.get("#results").should("contain", "Book 1")

從 Mirage 的角度來看,此測試包含兩個通常不應混合的抽象層級:首先是可見的 UI 元素和使用者互動,這些是從實際終端使用者的角度編寫的(例如 type('Book')(#results).should('contain', 'Book 1')),其次是 HTTP alias (wait('@getSearch')),它是此頁面的實作細節,實際上對終端使用者不可見。

Mirage 建議以單一抽象層級編寫 UI 測試,通常專注於終端使用者的真實世界行為。在等待 HTTP 回應的情況下,最好讓你的測試等待一些 UI 部分,以指示請求是處於擱置狀態還是已完成,而不是像為頁面提供動力的特定 HTTP 請求這樣的實作細節。因此,我們認為 Cypress 的 alias API 可能會導致脆弱的測試,使你的程式碼更難重構。

這些差異僅與 Cypress 的 HTTP 模擬層有關 – 總體而言,我們非常喜歡 Cypress!而且當與你的 Cypress 測試程式碼一起用作 API 模擬層時,Mirage 的效果很好。事實上,我們甚至有一個快速入門指南,介紹如何在 Cypress 應用程式中設定 Mirage

其他差異包括 Cypress 的 API 模擬通常不會在開發中共享,這是 Mirage 核心價值主張的重要組成部分。此外,在撰寫本文時,Cypress 不會攔截 fetch 請求,而 Mirage 會。


如果你好奇 Mirage 如何與此處未列出的其他工具進行比較,請隨時提出 issue