与其他工具比较

以下是 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

  • 它是一个真正的服务器,因此您可以在 devtools 中使用网络选项卡以及 Paw 或 curl 等其他 HTTP 工具来发送请求并检查响应。

  • 它是在 node 和 express 中编写的,因此您获得了 node 生态系统的全部功能。

  • 它开箱即用地提供了过滤和排序实现(虽然同样,它们不太可能与您的生产 API 相匹配)。

MSW

MSW 是一个模拟库,可以在浏览器或 node 中运行。

虽然 Mirage 和 MSW 都在客户端与您的前端代码一起运行,但主要区别在于 MSW 使用 Service Worker 来拦截网络请求,而 Mirage 使用 Pretender.js,它通过修补 window.fetchwindow.XMLHttpRequest 来工作。

使用 Service Worker 的主要优势是请求显示在 DevTools 的 Network 选项卡中 - 从浏览器的角度来看,它们是真实的 HTTP 请求。这使得 MSW 检查响应的开发人员体验更接近于与真实服务器端 API 交互的体验。

MSW 的一些其他优势

  • 它可以在 node 中运行,这意味着您的模拟可以在非浏览器 node 环境中共享。Mirage 也可以在 node 中运行,但只能在类似浏览器的环境中运行(即在类似 jsdom 的东西存在的情况下,Jest 等工具为您配置)。目前,Mirage 不能用于模拟 React 应用程序的服务器端 API 请求,例如。

  • 它有第一方 API 来帮助你模拟 GraphQL。 (Mirage 也能用于模拟 GraphQL,只是目前库中还没有第一方 API 来帮助你这样做。)

与 MSW 相比,Mirage 的主要优势类似于它在其他仅提供低级拦截器功能的库中的优势。MSW 的主要 API 是一个路由处理程序,它提供了一个请求并让你编写响应。Mirage 提供了一个类似的路由处理程序 API,可以以完全相同的方式使用,但它还带来了一个 DB,关系支持,用于轻松创建不同数据场景的工厂,一个序列化层来帮助你将 JSON 负载的形状与生产 API 匹配,以及一个服务器实例,使你在测试中轻松断言 DB 状态和变异。

在连接任何拦截器(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 是一个很棒的库,服务工作者概念甚至可能会在某个时候被纳入 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 的网络模拟基于静态夹具(通常是 JSON 文件),因此会遇到与其他工具相同的问题,这些工具没有使用数据库来确保跨 CRUD 操作的资源一致性。

Cypress 还具有别名 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 别名 (wait('@getSearch')),这是此页面的实现细节,实际上对最终用户不可见。

Mirage 建议以单一抽象级别编写 UI 测试,通常专注于最终用户的真实行为。在等待 HTTP 响应的情况下,最好让你的测试等待指示请求是否正在进行或已完成的 UI 部分,而不是等待像为页面提供动力的特定 HTTP 请求这样的实现细节。出于这个原因,我们认为 Cypress 的别名 API 会导致脆弱的测试,使你的代码更难重构。

这些差异只是关于 Cypress 的 HTTP 模拟层 - 总体而言,我们非常喜欢 Cypress!并且 Mirage 在用作你的 Cypress 测试代码旁边的 API 模拟层时效果很好。事实上,我们甚至有一个关于在 Cypress 应用程序中设置 Mirage 的快速入门指南

其他差异包括 Cypress 的 API 模拟通常不会在开发中共享,这是 Mirage 的核心价值主张的重要组成部分。此外,截至撰写本文时,Cypress 不会拦截 fetch 请求,而 Mirage 会。


如果你好奇 Mirage 与这里未列出的其他工具相比如何,请随时通过 打开问题 来询问!