6
2

Mock Service Workerを使ってみた

Last updated at Posted at 2023-12-19

この記事は、Supershipグループ Advent Calendar 2023の19日目の記事になります。

はじめに

Mock Service Workerの紹介の記事になります。

Mock Service Workerとは

モックのライブラリで、よくあるモックのライブラリより便利だった点は以下です

  • アプリケーション側の通信するコードを書き換えずにモックができる

    • アプリケーション側の通信する処理のコードで例えば環境変数だったりで、テスト時には通信しないというような分岐のコードなどの書き換えが必要がなかった
  • モックサーバーを別に起動する必要がない

    • 開発時にでモック用のサーバーが必要になった場合、モック用のサーバー作って別に立ち上げるという流れが多いがブラウザやNode.jsからそのまま動かせる

テストコードで使う

package.json
{
  "scripts": {
    "test": "vitest"
  },
  "dependencies": {
    "msw": "2.0.11",
    "vitest": "1.0.4"
  }
}

vitestのspyOnでモックした例

import { afterEach, vi, describe, expect, test } from 'vitest'

const Api = {
  fetchPost: async () => {
    const response = await fetch('https://text.example.com/posts')
    return await response.json()
  }
}

describe('post', () => {
  const response = {
    posts: [{ id: '1', name: 'name1' }, { id: '2', name: 'name1' }]
  }

  afterEach(() => {
    vi.resetAllMocks()
  })

  test('get posts', async () => {
    vi.spyOn(Api, 'fetchPost').mockResolvedValue(response)
    expect(await Api.fetchPost()).toEqual(response)
  })
})
  • 外部のAPIに対して通信する処理があるケースでは、テスト実行する毎に実際に通信するわけにもいかないのでモックすることが多いですが、その場合の課題点としてモックした部分に fetchPost に独自ロジックが入ってた場合にその部分のテストができない点があります。

  • vi.spyOn(global, 'fetch')fetch 自体もモックしてしまう手もありますが影響範囲が大きくなってしまう課題点があります。

Mock Service Workerを使った例

import { beforeAll, afterEach, afterAll, describe, expect, test } from 'vitest'
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

const Api = {
  fetchPost: async () => {
    const response = await fetch('https://text.example.com/posts')
    return await response.json()
  }
}

describe('posts', () => {
  const response = {
    posts: [{ id: '1', name: 'name1' }, { id: '2', name: 'name1' }]
  }

  const server = setupServer(
    http.get('https://text.example.com/posts', () => {
      return HttpResponse.json(response)
    })
  )

  beforeAll(() => server.listen())
  afterEach(() => server.resetHandlers())
  afterAll(() => server.close())

  test('get posts', async () => {
    expect(await Api.fetchPost()).toEqual(response)
  })
})

Mock Service Workerを使うと、元々の通信する処理に fetchPost に対して手を加えずにモックできるので、fetchPostの中にロジックが書かれていてもそこもそのままテストできます。

開発時にモックとして使う

package.json
{
  "scripts": {
    "start": "vite"
  },
  "dependencies": {
    "msw": "2.0.11",
    "vite": "5.0.10"
  }
}
mock.js
import { http, HttpResponse } from 'msw'
import { setupWorker } from 'msw/browser';

const response = {
  posts: [{ id: '1', name: 'name1' }, { id: '2', name: 'name1' }]
}

export const worker = setupWorker(
  http.get('https://text.example.com/posts', () => {
    return HttpResponse.json(response)
  })
)
index.js
const prepare = async () => {
  if (import.meta.env.VITE_MOCK_API === 'true') {
    const { worker } = await import('./mock')
    worker.start()
  }
}

const main = async () => {
  const response = await fetch('https://text.example.com/posts')
  await response.json()
}

window.onload = () => {
  prepare().then(async () => await main())
}

開発時にモックとして使う場合

  • 環境変数でモックの有効、無効を切り替えるようにしておく

    • プロダクション環境でもモックのままになってしまうので
  • モックのコードはDynamic Importするようにしておく

    • Dynamic ImportにしておくとJavaScriptをビルドした時のバンドルが分かれるので、プロダクション環境のデプロイ時にはモックのバンドルはアップロードする必要がないため
yarn run msw init ./public
VITE_MOCK_API=true yarn run start
VITE v5.0.10  ready in 495 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

スクリーンショット 2023-12-18 23.35.40.png

このようなかたちで実際のurlのサイトにはアクセスせず、アクセスしたようにレスポンスを返せます。

最後に

色々と活用できそうなので、興味ある方は試してみると良いかと思います。

  • 送ってきたパラメータによって返すモックのレスポンスを変える
  • apiとfrontendの担当者が別の場合に先にモックのapiを作ってしまってapiの実装完了待ちせずに画面の実装を並行して開発を進める

最後に宣伝です。
Supershipではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。
Supership 採用サイト
是非ともよろしくお願いします。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2