Mock Service WorkerでAPIのモックを作成

「Mock Service Worker」は、Service Workerを利用したモックライブラリです。 「REST API」「GraphQL API」の両方をサポートしています。ここでは、ReactアプリにMock Service Workerを導入する方法を確認します。

目次

インストール

create-react-app でReactプロジェクトを生成します。

npx create-react-app my-app --template typescript
cd my-app/

Mock Service Worker をインストールします。

yarn add msw --dev

バージョンは以下の通りです。

$ yarn info msw | grep 'version:'
  version: '0.21.3',

フォルダ構造・コード

フォルダ構造

今回、以下フォルダ構造で実装しました。

├──public
│   ├── index.html
│   └── mockServiceWorker.js    // コマンド(npx msw init public/)で生成したファイル
└──src
    ├── mocks                   
    │   ├── browser.ts          // ワーカーインスタンスを作成
    │   ├── handlers.ts         // 「リクエストパス」と「リクエストに応答する関数」を紐付け
    │   └── resolvers
    │       └── mockUser.ts     // リクエストに応答する関数
    ├── App.tsx                 // fetchを行うサンプルコンポーネント
    └── index.tsx               // ワーカーインスタンスをアプリケーションのコードにインポート

src/App.tsx

動作確認用コードです。

import React, { FC, useEffect, useState } from 'react';

export type User = {
  id: number;
  username: string;
  age: number;
};

const isUser = (params: unknown): params is User => {
  const user = params as User;

  return (
    typeof user?.id === 'number' &&
    typeof user?.username === 'string' &&
    typeof user?.age === 'number'
  );
};

const statusValues = {
  Loading: 'loading',
  Success: 'success',
  Error: 'error',
} as const;

type Status = typeof statusValues[keyof typeof statusValues];

const App: FC = () => {
  const [user, setUser] = useState<User | null>(null);
  const [status, setStatus] = useState<Status>(statusValues.Loading);

  useEffect(() => {
    const load = async (): Promise<void> => {
      try {
        const response = await fetch('http://localhost:3001/v1/users/123');

        if (!response.ok) {
          throw new Error(`HTTP-Error: ${response.status}`);
        }

        const responseData = (await response.json()) as unknown;
        if (!isUser(responseData)) {
          throw new Error(`Response Invalid: ${JSON.stringify(responseData)}`);
        }

        setUser(responseData);
      } catch (err) {
        setStatus(statusValues.Error);
        throw err;
      } finally {
        setStatus(statusValues.Success);
      }
    };

    void load();
  }, []);

  if (status === statusValues.Loading) return <p>Loading...</p>;
  if (status === statusValues.Error) return <p>Error</p>;

  return (
    <>
      <p>id: {user?.id}</p>
      <p>username: {user?.username}</p>
      <p>age: {user?.age}</p>
    </>
  );
};

export default App;

fetchした際、モックデータが返されるように実装していきます。

src/mocks/resolvers/mockUser.ts

リクエストに応答する関数を記述しています。

import { ResponseResolver, MockedRequest, restContext } from 'msw';
import { User } from 'App';

const mockUser: ResponseResolver<MockedRequest, typeof restContext> = (req, res, ctx) => {
  const userId = Number(req.params.userId);
  const user: User = {
    id: userId,
    username: 'wakuwaku bank',
    age: 18,
  };

  return res(ctx.json(user));
};

export default mockUser;

src/mocks/handlers.ts

mockUser.ts をインポートして、リクエストパスと紐づけています。

import { rest } from 'msw';
import mockUser from 'mocks/resolvers/mockUser';

const handlers = [
  rest.get('/v1/users/:userId', mockUser),
];

export default handlers;

src/mocks/browser.ts

handlers.ts をインポートして、ワーカーインスタンスを作成しています。

import { setupWorker } from 'msw';
import handlers from 'mocks/handlers';

const worker = setupWorker(...handlers);

export default worker;

src/index.tsx

開発環境のときだけ worker.start() します。

import React from 'react';
import ReactDOM from 'react-dom';

import App from 'App';
import worker from 'mocks/browser';

if (process.env.NODE_ENV === 'development') {
  void worker.start();
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root'),
);

動作確認

yarn start で起動して動作確認します。モックデータは Service Worker によって返されるので、別途専用のモックサーバーを起動する必要はありません。

モックサービスワーカーがactivateされたら、[MSW] Mocking enabled.と表示されます。

ネットワークタブです。Service Workerを利用しているので、from ServiceWorkerと表示されます。

Service Workerのイベント監視はコマンド( npx msw init public/ )で生成したpublic/mockServiceWorker.jsで実装されています。

参考・関連

よかったらシェアしてね!
目次