🐈

Mock Service Workerを利用したGraphQL開発環境構築

2023/02/19に公開

GraphQLサーバー構築が間に合わない、先にフロントエンド開発しておきたいという時ってありますよね。
そんな時に Mock Service Worker(以下、MSWと略) を使用してモック環境作成が可能です。

今回はMSW+GraphQLの環境構築をご紹介します。

環境

"msw": "^1.0.1"
"@mswjs/data": "^0.11.2"
"react": "^18.2.0"
"@apollo/client": "^3.7.7"

MSWについて

実際にモックサーバーを立ち上げるわけではなく、サービスワーカーレベルでリクエストを奪い、レスポンスを返すようになります。
https://mswjs.io/
実際のAPIを叩かないので、サーバー負荷をかけることがなく、サーバー状態でAPIが使用できなくなることもなくなります。

Reactプロジェクト作成

Reactプロジェクトを作成します。ここではTypeScriptを使用することにします。

npx create-react-app myapp --template typescript

モックサーバーの作成

実際にMSWを使用して、モックサーバーを作成しましょう。

mswjs/data

mswjs/dataライブラリを使用してハンドラを立てていきます。
以下、公式の説明です。

ハードコーディングされた一連のフィクスチャを保持する代わりに、このライブラリは、データ駆動型 API モックに必須のツールを提供します。
https://github.com/mswjs/data#motivation

簡単にいうと、mswjs/dataを使えば仮想DBをブラウザ展開して、実際のDBを扱うように操作できるよっていう感じでしょうか。

ハンドラ登録

factory関数を使用しデータをモデリングしていきます。

mocks/handlers.ts
import { factory, primaryKey } from "@mswjs/data";

const db = factory({
  todo: {
    id: primaryKey(String),
    title: String,
  },
});

export const handlers = [...db.todo.toHandlers("graphql")];

ここでtoHandlersが出てきました。これも便利なAPIの1つです。
todoモデルに対し、以下のように自動でCRUDハンドラが生成されます。

  • todo(where: TodoQueryInput): Todo
  • createTodo(data: TodoInput!): Todo!
  • updateTodo(where: TodoQueryInput!, data: TodoInput!): Todo!
  • deleteTodo(where: TodoQueryInput!): Todo!

このAPIを使えばCRUDハンドラを簡単構築できます。

以下のように、自分で定義が可能です。

export const handlers = [
  graphql.query('GetTodo', (req, res, ctx) => {
    return res(ctx.data({
      todos: db.todo.getAll()
    }))
  }),
]

ハンドラ設定

ブラウザ、Node.js環境で使用するハンドラ設定をします。

mocks/browser.ts
import { setupWorker } from "msw";
import { handlers } from "./handlers";

export const worker = setupWorker(...handlers);
mocks/server.ts
import { setupServer } from "msw/node";
import { handlers } from "./handlers";

export const server = setupServer(...handlers);

Service Workerの生成

さいごにMSWが使用するService Workerを生成する必要があります。
今回はpublic公開ディレクトリに作成します。

npx msw init public --save

するとpublic/mockServiceWorker.jsが作成されます。

開発環境で起動してみる

開発モードのみ使用したいので、環境変数を利用します。

src/index.tsx
...
if (process.env.NODE_ENV === "development") {
  const { worker } = require("./mocks/browser");
  worker.start();
}
...

実際に起動させてみます。
するとワーカーが立ち上がっているのがコンソールで確認できます。

テスト起動

Node.js環境なので、先ほど作成したserver.tsを使用します。
テスト毎にモックサーバーが立ち上がり、終われば停止させるように設定します。

src/setupTests.js
...
import { client } from "./libs/apollo";
import { server } from "./mocks/server";

beforeAll(() => {
  server.listen();
});
beforeEach(() => {
  return client.clearStore();
});
afterEach(() => {
  server.resetHandlers();
});
afterAll(() => {
  server.close();
});

シーディング

モックデータを生成したい場合にはORM風にデータ生成が可能です。

const db = factory({
  todo: {
    id: primaryKey(String),
    title: String,
  },
});

for (let i = 0; i < 3; i++) {
  db.todo.create({
    id: Math.random().toString(32).substring(2) ,
    title: `todo-${i}`
  })
}

さいごに

MSWはテスト環境だけでなく開発環境などでも使用できます。
REST APIだけでなくGraphQLのモック環境も作成できるので、ぜひ活用してみてください。

作成リポジトリ

今回作成したコードを載せています。
https://github.com/k-logic563/msw-graphql

参考記事

https://mswjs.io
https://zenn.dev/azukiazusa/articles/using-msw-to-mock-frontend-tests
https://zenn.dev/takepepe/articles/msw-data-userflow-testing

Discussion