建構完整的前端功能, 即使您的 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 框架、函式庫和測試執行器搭配使用。