Mock Service Worker (MSW) とは、Service Woker が API リクエストを受け取って、レスポンスを返すことができる API mock ライブラリです。
Introduction - Mock Service Worker Docs
MSW を使えば、Storybook とテストで共通の API mock handler を使用することができて便利です。
Storybook で MSW を使用するにあたっていくつか設定が必要になります。
調べると 1 コンポーネントしかないような小さな example は出てくるのですが、コンポーネント毎に mock を分けられるような方法にしたかったので、今回はその手順を記録しておきます。
(本記事は Storybook 6.3.4, MSW 0.33.2 で動作を確認していますが、別のバージョンだと動作しない可能性があります。
その場合、msw-storybook-addon
など別の方法で代替することができます。)
基本的な使い方
Storybook との連携に入る前に、MSW の基本的な使い方について軽く触れておきます。
以下は公式ページに記載していたコード例です。
1// src/mocks.js2import { setupWorker, rest } from 'msw'34const worker = setupWorker(5rest.post('/login', (req, res, ctx) => {6const isAuthenticated = sessionStorage.getItem('username')78if (!isAuthenticated) {9return res(10ctx.status(403),11ctx.json({12errorMessage: 'Not authenticated',13}),14)15}1617return res(18ctx.json({19firstName: 'John',20}),21)22}),23)2425// Register the Service Worker and enable the mocking26worker.start()
setupWorker()
で Service Worker をセットアップし、worker.start()
で立ち上げて、API mock を有効にします。
setupWorker()
の引数にリクエスト形式、URL とレスポンス(request handlers)を指定します。
使用する際には npx msw init
コマンドで mockServiceWorker.js
を生成し、これを public
ディレクトリなど、ルートパスから /mockServiceWorker.js
としてアクセスできるディレクトリに配置する必要があります。
(これを知らずに 404 error を出して起動できないことがよくありました)
Storybook で使うための設定をする
ここから Storybook で MSW を使えるようにしていきます。
mockServiceWorker.js
を生成
まずは必要となる mockServiceWorker.js
を生成します。
.storybook/public/
ディレクトリを作成し、これを static directory に指定します。
プロジェクトルートで以下のコマンドを入力します。
npx msw init .storybook/public
すると .storybook/public/mockServiceWorker.js
が生成されます。
Storybook の static directory を指定
Storybook においてルートパスからアクセスできるディレクトリ(static directory)は main.js
において staticDirs
を指定するか、-s
フラグを指定することで設定できます。
(-s
フラグは 6.4 から deprecated になり、main.js
での設定に移行します。)
.storybook/main.js
に設定を追加するか、
.storybook/main.js1module.exports = {2stories: [],3addons: [],4staticDirs: ['./public'],5}
package.json
の npm script を変更します。
package.json1{2"scripts": {3- "storybook": "start-storybook"4+ "storybook": "start-storybook -s .storybook/public"5}6}
これで Storybook を立ち上げたときに mockServiceWorker.js
が参照できるようになるはずです。
Storybook 起動時に Service Worker を立ち上げる
先述の通り、mock を開始するには setupWorker()
と worker.start()
が必要です。
Storybook を立ち上げたときにすべての story に適用するため .storybook/preview.js
で設定します。
.storybook/preview.js1import { setupWorker } from 'msw';23// Node 環境ではなくブラウザ環境にいることをチェック4if (typeof global.process === 'undefined') {5// MSW をセットアップ6const worker = setupWorker();7// Service Worker を立ち上げる8worker.start();9// stories ファイルからアクセスできるように、worker をグローバルに参照できるようにする10window.msw = { worker };11}
うまくいけば Storybook を立ち上げたときの console に [MSW] Mocking enabled.
と表示されます。
現時点では setupWorker()
の引数がないのですが、worker.use()
を使うことで request handler を追加することができます。
MSW と Storybook 自体の設定はこれで完了です。
コンポーネントの stories ファイルで使う
TypeScript の場合、window.msw
としてアクセスしたときに TypeScript でエラーになってしまうので、まず型定義を追加しておきます。
global.d.ts1import type { SetupWorkerApi } from 'msw';23declare global {4interface Window {5msw: { worker: SetupWorkerApi };6}7}
次に各コンポーネントに request handlers と stories ファイルを配置します。
以下は handlers の例です。
ComponentWithAPICall/mocks/handlers.ts1import { rest } from 'msw';2import type { GetResponse, GetResponseError } from '~/path/to/api/types';34const getUrl = '/api/path/to/get';56const getResponseExample: GetResponse = {7data: 'something',8};910const getResponseErrorExample: GetResponseError = {11message: 'failed',12};1314export const handlers = {15default: rest.get(getUrl, (req, res, ctx) => {16return res(17ctx.delay(1000),18ctx.status(200),19ctx.json(getResponseExample)20);21}),22error: rest.get(getUrl, (req, res, ctx) => {23return res(24ctx.delay(1000),25ctx.status(500),26ctx.json(getResponseErrorExample)27);28}),29};
stories ファイルでの使用例は以下のようになります。
ComponentWithAPICall/ComponentWithAPICall.stories.tsx1import { handlers } from './mock/handlers';23const DefaultTemplate: ComponentStory<typeof ComponentWithAPICall> = (args) => {4const { worker } = window.msw;56useEffect(() => () => worker.resetHandlers());78worker.use(handlers.default);910return <ComponentWithAPICall {...args} />;11};1213const ErrorTemplate: ComponentStory<typeof ComponentWithAPICall> = (args) => {14const { worker } = window.msw;1516useEffect(() => () => worker.resetHandlers());1718worker.use(handlers.error);1920return <ComponentWithAPICall {...args} />;21};
テストのときも同じ handlers
を使いまわせます。
ComponentWithAPICall/ComponentWithAPICall.test.tsx1import { setupServer } from 'msw/node';2import { handlers } from './mock/handlers';34const server = setupServer();56...7describe('when API request succeeded', () => {8beforeEach(() => server.resetHandlers(handlers.default));9...10})1112describe('when API request failed', () => {13beforeEach(() => server.resetHandlers(handlers.error));14...15})
この記事が参考になったならうれしいです。
では
参考
How to mock APIs in Storybook with MSW (Mock Service Worker) | by Alexis Oney | Medium