🛵

StorybookでMock Service Worker (MSW) を使えるようにする。

2022/05/29に公開

この記事は、Storybookの component story format 3.0 で利用できるようになったPlay functionsでmockサーバーを利用するために Mock Service Worker (MSW) を使えるようにするための議事録です。

今回は、msw-storybook-addon というアドオンを適応します。
https://storybook.js.org/addons/msw-storybook-addon

MSWとアドオンをインストールする

yarn add msw msw-storybook-addon -D

パブリックフォルダーにMSWのServiceWorkerを生成する

サービスワーカーのセットアップを実施します。
一から記述するのは大変面倒なので、mswが提供しているinitを利用します。

npx msw init public/ --save

アドオンを構成する

.storybook/preview.jsでMSWを初期化し、mswDecoratorを用いてStorybookでMSWを利用できるようにします。

.storybook/preview.js
import { initialize, mswDecorator } from 'msw-storybook-addon';

// Initialize MSW
initialize();

// Provide the MSW addon decorator globally
export const decorators = [mswDecorator];

Storybookのstatic directoryを指定

Storybook がルートパスからアクセスできるディレクトリ(static directory)を指定します。
今回は、public/です。.storybook/main.jsstaticDirsに設定します。

.storybook/main.js
module.exports = {
  // ...
  staticDirs: ['../public'],
};

https://storybook.js.org/docs/react/configure/images-and-assets#serving-static-files-via-storybook-configuration

MSWが動いているかどうか確認する

設定したMSWが起動されているかどうか確認します。

yarn storybook
# Storybookを立ち上げるためのコマンド

立ち上がったStorybook上のdevtoolコンソールで、下記のメッセージが表示されていれば成功です。

[MSW] Mocking enabled.

リクエストハンドラーを設定する

リクエストハンドラーを個別に設定する場合は、ストーリーコンポーネントのparametersに設定を渡します。
handlersには、配列かObjectで渡すことができます。

import { ComponentStory } from '@storybook/react'
import { rest } from 'msw'

export const SuccessBehavior: ComponentStory<typeof UserProfile> = (args) => <UserProfile  {...args} />

SuccessBehavior.parameters = {
  msw: {
    handlers: [
      rest.get('/user', (req, res, ctx) => {
        return res(
          ctx.json({
            firstName: 'Neil',
            lastName: 'Maverick',
          })
        )
      }),
    ]
  },
}

共通のリクエストハンドラーを設定する

ほとんどのコンポーネントが/loginなどをモックする必要がある場合、preview.jsでグローバルなリクエストハンドラーを設定することができます。

.storybook/preview.js
// These handlers will be applied in every story
export const parameters = {
  // ...
  msw: {
    handlers: {
      auth: [
        rest.get('/login', (req, res, ctx) => {
          return res(
            ctx.json({
              success: true,
            })
          )
	}),
        rest.get('/logout', (req, res, ctx) => {
          return res(
            ctx.json({
              success: true,
            })
          )
        }),
      ],
    }
  }
};

個々のストーリーコンポーネントで他のリクエストハンドラーを使用できます。
個々で設定したリクエストハンドラーはグローバルに設定したリクエストハンドラーとマージされます。

// This story will include the auth handlers from preview.js and profile handlers
SuccessBehavior.parameters = {
  msw: {
   handlers: {
    profile: rest.get('/profile', (req, res, ctx) => {
      return res(
       ctx.json({
        firstName: 'Neil',
        lastName: 'Maverick',
       })
      )
    }),
   }
  }
}

特定のリクエストハンドラーにオーバーライトする

グローバルに設定したリクエストハンドラーを特定のストーリーコンポーネントで書き換えたい場合は、特定のkeyに合わせるよう設定することでオーバーライトすることができます。

// This story will overwrite the auth handlers from preview.js
FailureBehavior.parameters = {
  msw: {
   handlers: {
    auth: rest.get('/login', (req, res, ctx) => {
      return res(ctx.status(403))
    }),
   }
  }
}

最後に

MSWは、通常のモックサーバーとしても利用できる為、テスト以外の目的でも便利に利用することができそうです。
今回初めてStorybookのformat 3.0を利用したのですが、ユーザの利用ケース事にテストを書くことができ、管理画面も提供できるので、そのまま仕様書として出しても申し分なさそうなものを作成することができました。

ハッカー飯で丁寧にテストについて教えていただいたてんかわさんにはすごく感謝です。🙌

次のTRYとして近いうちに、chromaticを用いたビジュアルリグレッションテストも実施してみたいと思います。

フロントエンドのテストもどこまでやるのがベストなのか、まだ模索していますが、これからもインプットとアウトプットしていきたいと思います。

参考

https://storybook.js.org/blog/component-story-format-3-0/
https://storybook.js.org/addons/msw-storybook-addon
https://mswjs.io/docs/getting-started/integrate/browser#where-is-my-public-directory
https://storybook.js.org/docs/react/configure/images-and-assets#serving-static-files-via-storybook-configuration
https://github.com/tenkawa0/frontend-test
https://zenn.dev/sa2knight/scraps/13bf492debd5e1
https://zenn.dev/midorimici/articles/msw-storybook
https://storybook.js.org/docs/react/api/cli-options#static-dir-deprecation

Discussion