💭

Mock Service Workerを導入してフロントエンド開発を効率化

に公開

Mock Service Workerとは

Mock Service Workerは、APIリクエストをキャッチしてモックデータを返してくれるライブラリです。
ブラウザ環境ではService Workerを使います。
実際のAPIを呼び出しているかのように動作するので、本番環境に近い形で開発ができます。
https://mswjs.io/

Mock Service Workerの特徴

本物のAPIリクエストと同じ形式でモックレスポンスが返るので、実際のAPIを使っているような感覚で、開発やブラウザでの動作確認が行えます。
ブラウザでは、Service Workerを使ってリクエストをキャッチします。
REST APIだけでなく、GraphQLにも対応しています。

導入手順

以下のような手順で、簡単にモック環境を構築できます。

インストール

まずMock Service Workerをプロジェクトに追加します。

# npmの場合
npm install msw --save-dev

# yarnの場合
yarn add -D msw

モックサーバーの準備

モック用の型定義、データ、ハンドラーを準備しブラウザ上で動作させます。

  1. 型定義の準備
    APIレスポンスの型定義を行います。
// src/mocks/types.ts
export interface User {
  id: string
  name: string
  email: string
}
  1. モックデータの定義
    ダミーデータを用意します。
    実際のAPIから返されるデータ構造と同様にする必要があります。
// src/mocks/data/users.ts
import { User } from '../types'

export const users: User[] = [
  {
    id: '1',
    name: 'Hoge Tarou',
    email: 'hoge@example.com',
  },
]
  1. ハンドラーの定義
    リクエストとレスポンスのパターンを記述します。
    REST APIのエンドポイントごとにハンドラーを用意します。
// src/mocks/handlers/users.ts
import { rest } from 'msw'
import { users } from '../data/users'

const BASE_URL = process.env.REACT_APP_API_BASE_URL // APIのbaseURLを環境変数から取得

export const userHandlers = [
  // ユーザー一覧取得
  rest.get(`${BASE_URL}/users`, (req, res, ctx) => {
    return res(ctx.status(200), ctx.json({ data: users })) // 一覧データを返す
  }),

  // ユーザー取得
  rest.get(`${BASE_URL}/users/:id`, (req, res, ctx) => {
    const { id } = req.params // パラメータからIDを取得
    const user = users.find((u) => u.id === id) // 該当ユーザーを検索

    if (!user) {
      return res(ctx.status(404), ctx.json({ error: 'User not found' })) // 見つからない場合
    }

    return res(ctx.status(200), ctx.json({ data: user })) // ユーザー情報を返す
  }),
]
  1. ハンドラーの集約
    複数のハンドラーを1つにまとめることで、管理しやすくなります。
// src/mocks/handlers/index.ts
import { userHandlers } from './users'
import { authHandlers } from './auth'

export const handlers = [...userHandlers, ...authHandlers] // ハンドラーをまとめる
  1. ブラウザ用のセットアップ
    実際にブラウザでService Workerを起動するための設定を行います。
// src/mocks/browser.ts
import { setupWorker } from 'msw'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers) // 全てのハンドラーを渡して初期化
  1. アプリでの初期化
    アプリケーション起動時にMock Service Workerを起動します。
    環境変数を使ってON/OFFを切り替えます。
// src/index.tsx
if (process.env.REACT_APP_USE_MSW === 'TRUE') {
  ;(async () => {
    const { worker } = await import('./mocks/browser')
    await worker.start() // Service Workerを起動
    console.log('Mock Service Worker started')
  })()
}

導入してみて良かったこと

1. 開発がかなり楽になった

実際に、以下のようなメリットがありました。

  • バックエンドの開発を待たなくても、フロントエンドの開発が進められる
  • APIの仕様が変わっても、モックを修正するだけで対応できる
  • 環境変数で簡単にオン/オフできる
  • 型定義を共有することで、フロントエンドとバックエンドで一貫性のある開発ができる

2. テストが書きやすくなった

フロントエンドの単体テストや統合テストでモックAPIを使うことで、安定したテスト環境を作れます。
テストを行う前に、モックサーバーをセットアップする必要があります。

// setupTests.ts
import { server } from './mocks/server' // サーバーハンドラーをインポート

beforeAll(() => server.listen()) // テスト開始前にモックサーバーを起動
afterEach(() => server.resetHandlers()) // 各テスト後にハンドラーをリセット
afterAll(() => server.close()) // テスト終了後にサーバーを停止

例えば以下のようなテストでは、UserListコンポーネント内でのAPIリクエストに対するレスポンスとして、Mock Service Workerのモックデータが提供されます。

// src/templates/List/index.test.tsx
import { render, screen } from '@testing-library/react'
import UserList from './UserList'

describe('UserList', () => {
  test('ユーザー一覧が表示される', async () => {
    render(<UserList />)
    // モックされたユーザーデータが表示されることを確認
    expect(await screen.findByText('Hoge Tarou')).toBeInTheDocument()
  })
})

このように、Mock Service Workerを使うことで、サーバーを実際に立ち上げなくてもフロントエンドの動作確認やテストが可能になります。

3. エラーパターンの動作確認

実際の運用を想定し、認証エラーやバリデーションエラー、タイムアウトなどのエラーケースもモックしておくと、ユーザー向けのエラーハンドリングが適切に動作しているか、リリース前にチェックできます。

運用する上で気をつけるポイント

1. モックデータの管理

以下のような点に気をつけると運用しやすいです。

  • 機能ごとにファイルを分けて整理(users, auth など)
  • 共通で使うモックデータは別ファイルで管理
  • 型定義をしっかり行い、本番APIとの一貫性を保つ
  • テスト用のモックデータは現実的なデータを使用

2. 環境の使い分け

本番環境でモックが動かないよう、環境ごとの切り替えを明確にしておきます。

  • 開発環境: Mock Service Workerを使用(REACT_APP_USE_MSW=TRUE
  • 本番環境: Mock Service Workerを無効化
  • E2Eテスト: 実際のAPIを使用

3. 開発時のパフォーマンスへの影響

開発環境で快適に動作するよう、パフォーマンスにも気をつけました。

  • 初期化処理を最小限にし、不要なハンドラーを読み込まない
  • 大きなモックデータは、必要な部分だけ動的に生成
  • 使わないAPIエンドポイントをhandlersから除外し、不要なモックを無効化
  • ブラウザのキャッシュを活用し、必要以上にリクエストを発生させない

まとめ

初めてMock Service Workerを使ってみましたが、とても便利でした!
以下のようなメリットがありました。

  • APIのモックが簡単に作れる
  • テストが書きやすい(APIサーバーが必要な環境を用意する必要がない)
  • バックエンドの開発を待たずにフロントエンドの開発を進められる
  • エラーケースのテストが容易

皆さんもぜひMock Service Workerを試してみてください!

Discussion