Closed6

[キャッチアップ] MSW

shingo.sasakishingo.sasaki

概要

  • MSW を使った API モックを雑に試す
  • Vite で立ち上げた Vue3 アプリケーションを使ってる

MSW について

MSW (Mock Service Worker) は、 ネットワークレベルで API をモックするための npm パッケージ。

ネットワークレベルというのが特徴で、利用側がモックの存在を意識せずとも、そのままのコードで API レスポンスだけを差し替えることができる。

利用側のコードを書き換える必要がないため、一つのモックで、開発環境、テスト環境、あるいは Storybook のようなサンドボックス環境でもそのまま利用することができる

shingo.sasakishingo.sasaki

素振り用のアプリを用意する

今回は、Vueアプリケーションの開発環境上で、サーバサイドが未完成のため、MSW によるモックを用いた開発をするという想定で試してみる。

単に素振り用 Vue アプリケーションがあれば充分なので、 Vite で開発環境を用意する。

$ yarn create @vitejs/app msw-test --template vue-ts

開発環境をセットアップして立ち上げる。必要なパッケージをついでに追加しておく。

$ cd msw-test
$ yarn install
$ yarn add axios
$ yarn add -D prettier
$ yarn dev

スキャフォールドされた App.vue を最低限の内容に書き換える

App.vue
<template>
  <div>Hello, MSW!!</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  name: "App",
});
</script>

これで準備OK

shingo.sasakishingo.sasaki

MSW のインストール

msw は開発、テスト用途なので devDependencies でインストール

$ yarn add -D msw
shingo.sasakishingo.sasaki

REST API のモックを作成する

msw は、 REST API または GraphQL API のモックを作成することができるが、本スクラップでは REST のみを扱う。

src/mocks/handlers.ts を作成し、ここに全てのモックの定義をまとめて書いていく。

src/mocks/handlers.ts
import { rest } from 'msw'

export const handlers = [
  rest.get('/users', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({
        users: [
          {
            id: 1,
            name: 'suzuki'
          },
          {
            id: 2,
            name: 'sato'
          }
        ]
      })
    )
  })
]

直感的に読めると思うが、ここでは /users にGET リクエストした際に、ステータスコード200で、2種類のユーザー情報を返却するようなモックを定義した

shingo.sasakishingo.sasaki

モックを ブラウザから利用する

モックの定義が完了したので、次はこれをブラウザ上で Service Worker を使って動かしていく。

なお、 MSW は Node 上でも動かすことが出来るが、 Node では Service Worker が使えないため、別の方法を用いることになるが、ここではブラウザ上でのみ動作確認を行う。

ワーカーのセットアップ

ここで立ち上げる Service Worker は、ブラウザ上でのリクエストを仲介し、モックレスポンスに差し替える役割を持つ。

そのために、ワーカーのコードを配置する必要があるが、これを自分で書くのは困難なので、 以下コマンドを用いて MSW から提供されるコードを複製する。

$ npx msw init public/ --save

これによって、 public/ ディレクトリに、ワーカーコードが作成される。 public/ ディレクトリはプロジェクトごとにパスが異なるが、ここでは Vite を使って Vue アプリケーションのスキャフォールドをしているので、 public/ が公開用ディレクトリとなる。

ワーカーとモックの紐付け

handler.ts と同じディレクトリに browser.ts を作成し、 setupWorker 関数を用いて、ワーカーとモックハンドラを紐付ける。

src/mocks/browser.ts
import { setupWorker } from 'msw'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)

ワーカーを動かす

ワーカーの準備が整ったので、アプリケーション側のコードでワーカーを開始する

src/App.vue
<template>
  <div>Hello, MSW!!</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'App',
  setup() {
    if (process.env.NODE_ENV === 'development') {
      import('./mocks/browser').then(module => {
        module.worker.start()
      })
    }
  }
})
</script>

コンソールに以下のメッセージが出れば成功

[MSW] Mocking enabled.

shingo.sasakishingo.sasaki

動作確認

App.vue を修正し、ワーカーが立ち上がったら axios/users を呼び出し、レスポンスを state に保存し、それをテンプレートで描画できるようにする。

src/App.vue
<template>
  <div>
    <ul>
      <li :key="user.id" v-for="user in state.users">
        <p>id: {{ user.id }}</p>
        <p>name: {{ user.name }}</p>
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue'
import axios from 'axios'
type User = { id: number; name: string }

export default defineComponent({
  name: 'App',
  setup() {
    const state = reactive({
      users: [] as User[]
    })

    if (process.env.NODE_ENV === 'development') {
      import('./mocks/browser').then(async module => {
        await module.worker.start()
        const response = await axios.get('/users')
        state.users = response.data.users
      })
    }

    return { state }
  }
})
</script>

モックされたユーザー一覧が描画されることが確認できた

このスクラップは2021/03/28にクローズされました