构建完整的 前端 功能, 即使你的 API 不存在。

Mirage JS 是一个 API 模拟库,它允许你构建、测试和共享一个完整的 JavaScript 应用程序,无需依赖任何后端服务。

创建服务器

Mirage 与你的其他 前端 JavaScript 代码一起运行 - 无需新的服务器进程或终端窗口。使用你熟悉的开发工具编写准备好网络的 UI 代码。

前端 开发的 最佳体验

告别仅仅为了修改 UI 就要配置麻烦的后端环境。Mirage 与你的 前端 代码一起运行,所以你无需学习新的基础设施。

新的协作者可以克隆你的 前端 代码库,运行 npm install,并在几秒钟内启动一个完整的本地离线开发环境 - 无需环境变量或身份验证令牌。

import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
import { createServer } from "miragejs"

createServer({
  routes() {
    this.namespace = "api"

    this.get("/todos", ({ db }) => {
      return db.todos
    })
  },
})

ReactDOM.render(<App />, document.getElementById("root"))

自 2015 年起,我们就在 Heroku 上的关键面向客户的应用程序中使用 Mirage。它使我们的团队能够在不牺牲开发或测试工作流程速度的情况下进行扩展。

对我来说,Mirage 的真正魔力在于它让我们能够从用户的角度编写测试。我们从产品团队获取用户故事,并将它们 1:1 翻译成测试,无需通过离开 前端 工具链来中断工作流程。

简而言之,Mirage 是每个 UI 开发人员必备的工具。

Jamie White

Salesforce/Heroku 软件工程师

编写 高级 UI 测试 它会给你的网络代码带来压力。

使用 Mirage,无论你的 API 处于何种状态,你都可以针对 API 编写自动化测试。测试你的应用程序如何处理 0 个博客文章、10 个或 1000 个 - 甚至测试它在服务器速度慢或响应错误时如何表现。

在你的测试中,没有混乱的模拟代码或手工制作的 API 响应。只有验证整个应用程序的全部功能的真实场景。

Browsers
it("shows a message if there are no todos", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getByTestId("no-todos")).toBeInTheDocument()
})

it("shows existing todos", async () => {
  server.createList("todo", 3)

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getAllByTestId("todo")).toHaveLength(3)
})

it("can complete a todo", async () => {
  server.create("todo", { text: "Todo 1", isDone: false })
  server.create("todo", { text: "Todo 2", isDone: false })

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))
  const todos = getAllByTestId("todo")
  userEvent.click(todos[1].querySelector("input[type='checkbox']"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  expect(todos[0].querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todos[1].querySelector('input[type="checkbox"]').checked).toBe(true)
  expect(server.db.todos[1].isDone).toBe(true)
})

it("can create a todo", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  const newTodoForm = await waitForElement(() => getByTestId("new-todo-form"))
  userEvent.type(newTodoForm.querySelector("input"), "Walk the dog")
  fireEvent.submit(getByTestId("new-todo-form"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  const todo = getByTestId("todo")
  expect(todo.querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todo.querySelector('input[type="text"]').value).toBe("Walk the dog")
  expect(server.db.todos.length).toBe(1)
  expect(server.db.todos[0].text).toBe("Walk the dog")
})

it("shows a message if there are no todos", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getByTestId("no-todos")).toBeInTheDocument()
})

it("shows existing todos", async () => {
  server.createList("todo", 3)

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  expect(getAllByTestId("todo")).toHaveLength(3)
})

it("can complete a todo", async () => {
  server.create("todo", { text: "Todo 1", isDone: false })
  server.create("todo", { text: "Todo 2", isDone: false })

  const { getByTestId, getAllByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))
  const todos = getAllByTestId("todo")
  userEvent.click(todos[1].querySelector("input[type='checkbox']"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  expect(todos[0].querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todos[1].querySelector('input[type="checkbox"]').checked).toBe(true)
  expect(server.db.todos[1].isDone).toBe(true)
})

it("can create a todo", async () => {
  const { getByTestId } = render(<App />)
  await waitForElementToBeRemoved(() => getByTestId("loading"))

  const newTodoForm = await waitForElement(() => getByTestId("new-todo-form"))
  userEvent.type(newTodoForm.querySelector("input"), "Walk the dog")
  fireEvent.submit(getByTestId("new-todo-form"))
  await waitForElementToBeRemoved(() => getByTestId("saving"))

  const todo = getByTestId("todo")
  expect(todo.querySelector('input[type="checkbox"]').checked).toBe(false)
  expect(todo.querySelector('input[type="text"]').value).toBe("Walk the dog")
  expect(server.db.todos.length).toBe(1)
  expect(server.db.todos[0].text).toBe("Walk the dog")
})

分享一个 完全可工作的 UI 无需运行后端。

由于 Mirage 真实地模拟了你的整个 API 服务器,因此你无需运行任何后端服务,就可以分享一个可点击的 JavaScript 应用程序工作原型。

在开始投资昂贵的后端基础设施之前,从你的用户那里获得高质量的反馈,并比以往更快地进行迭代。

老实说,我强烈推荐这款工具。终于有了一种 前端 开发人员可以用来原型化和测试整个功能而无需触碰真实 API 的惯用方法!效率直接飙升。

Roman Sandler

500tech 开发人员

开始使用 Mirage

Mirage 与所有主要的 JavaScript 框架、库和测试运行器兼容。