Mirage を使用した Cypress でのモックネットワークリクエスト
Cypress を使用して、さまざまなサーバーシナリオでアプリケーションをテストするために Mirage サーバーを使用します。
これは、アプリケーションで既に Cypress を使用しているユーザー向けのクイックスタートガイドです。
ステップ1:Mirage のインストール
まず、Mirage がインストールされていることを確認してください。
# Using npm
npm install --save-dev miragejs
# Using Yarn
yarn add --dev miragejs
ステップ2:サーバーの定義
新しいsrc/server.js
ファイルを作成し、モックサーバーを定義します。
基本的な例を以下に示します。
// src/server.js
import { createServer, Model } from "miragejs"
export function makeServer({ environment = "development" } = {}) {
let server = createServer({
environment,
models: {
user: Model,
},
seeds(server) {
server.create("user", { name: "Bob" })
server.create("user", { name: "Alice" })
},
routes() {
this.namespace = "api"
this.get("/users", (schema) => {
return schema.users.all()
})
},
})
return server
}
ステップ3:アプリケーションの API リクエストに対するプロキシ関数を Cypress で定義する
次のコードをcypress/support/index.js
ファイルに追加します。
// cypress/support/index.js
Cypress.on("window:before:load", (win) => {
win.handleFromCypress = function (request) {
return fetch(request.url, {
method: request.method,
headers: request.requestHeaders,
body: request.requestBody,
}).then((res) => {
let content = res.headers.get("content-type").includes("application/json")
? res.json()
: res.text()
return new Promise((resolve) => {
content.then((body) => resolve([res.status, res.headers, body]))
})
})
}
})
このコードは、アプリケーションのwindow
オブジェクトにhandleFromCypress
関数を定義します。次のステップでは、Cypress が実行されている間にネットワークリクエストを行うたびに、この関数を呼び出すようにアプリケーションを構成します。
ステップ4:アプリケーションのネットワークリクエストをプロキシする
アプリケーションのブートストラップファイルで、Cypress が実行されている場合、Mirage を使用してアプリケーションの API リクエストを前のステップで定義したhandleFromCypress
関数にプロキシします。
Create React App ユーザーの場合、このコードは
src/index.js
に入ります。
Vue CLI ユーザーの場合、このコードは
src/main.js
に入ります。
import { createServer, Response } from "miragejs"
if (window.Cypress) {
// If your app makes requests to domains other than / (the current domain), add them
// here so that they are also proxied from your app to the handleFromCypress function.
// For example: let otherDomains = ["https://my-backend.herokuapp.com/"]
let otherDomains = []
let methods = ["get", "put", "patch", "post", "delete"]
createServer({
environment: "test",
routes() {
for (const domain of ["/", ...otherDomains]) {
for (const method of methods) {
this[method](`${domain}*`, async (schema, request) => {
let [status, headers, body] = await window.handleFromCypress(
request
)
return new Response(status, headers, body)
})
}
}
// If your central server has any calls to passthrough(), you'll need to duplicate them here
// this.passthrough('https://analytics.google.com')
},
})
}
これで、Cypress がアプリケーションを起動するたびに、このコードはアプリケーションのネットワークリクエストを前のステップで定義したhandleFromCypress
関数に委任します。
実際の構成済みの Mirage サーバーを Cypress コードと一緒に起動すると、その関数からのリクエストのインターセプトを開始します。
ステップ5:Mirage サーバーを使用してテストを作成する
新しいcypress/integration/app.spec.js
ファイルを作成し、makeServer
関数をインポートし、各テストの前後に Mirage を起動およびシャットダウンします。その後、各テストで異なるデータシナリオを使用して Mirage にシードし、テストを使用して UI の状態を検証できます。
import { makeServer } from "../../src/server"
describe("user list", () => {
let server
beforeEach(() => {
server = makeServer({ environment: "test" })
})
afterEach(() => {
server.shutdown()
})
it("shows the users from our server", () => {
server.create("user", { id: 1, name: "Luke" })
server.create("user", { id: 2, name: "Leia" })
cy.visit("/")
cy.get('[data-testid="user-1"]').contains("Luke")
cy.get('[data-testid="user-2"]').contains("Leia")
})
it("shows a message if there are no users", () => {
// Don't create any users
cy.visit("/")
cy.get('[data-testid="no-users"]').should("be.visible")
})
})
Mirage ではmakeServer
関数にenvironment: test
オプションを渡すため、Mirage はデータベースシードを読み込みません。そのため、サーバーは各テスト実行時に空の状態から開始され、テストの開始時にserver.create
を使用してデータシナリオを設定できます。テスト環境では、ログとレイテンシも無効になっているため、デフォルトで CI テストログはクリーンになり、テストは高速になります。
また、Cypress のdescribe
ブロックの使用にも注意してください。これにより、より多くのファイルを追加しても、Mirage サーバーは各仕様にスコープされ、テストファイル間の状態のリークを防ぎます。
ステップ6:異なるサーバーの状態をテストするために Mirage サーバーを変更する
異なるデータシナリオに加えて、テストを使用して Mirage サーバーを再構成し、新しい状況をテストできます。
たとえば、次のようにエラー状態をテストできます。
import { Response } from "miragejs"
it("handles error responses from the server", () => {
// Override Mirage's route handler for /users, just for this test
server.get("/users", () => {
return new Response(500, {}, { error: "The database is on vacation." })
})
cy.visit("/")
cy.get('[data-testid="server-error"]').contains(
"The database is on vacation."
)
})
Mirage が Cypress と統合される方法により、各テストではメインサーバーの定義に基づいて新しい Mirage サーバーが取得されます。テスト内で行ったオーバーライドは、そのテストに限定されます。