【msw】APIをStorybookでモックしてテストしてみる

# はじめに

こんにちは、エンジニアのkeitaMaxです。

今回はmswを使用してStorybookでAPIを呼び出すテストを書いていこうと思います。

Laravel10,Inertia.js,React,Storybookを使用します。

# インストール

以下コマンドでインストールします。

```
npm install msw@latest --save-dev
npm i msw msw-storybook-addon -D
```

# mswの初期設定

以下コマンドで初期設定します。

まず`msw`ファイルを`public`と同じ階層に作成します。

その後以下のコマンドを実行します。

```
npx msw init msw/ --save
```

すると`msw`フォルダ配下に`mockServiceWorker.js`ファイルが作成されます。

※これしないと、以下のようにbuildする時に怒られます。
```
(!) The public directory feature may not work correctly. outDir /Users/niheikeita/develop/real-estate-search-app/src/public/build and publicDir /Users/niheikeita/develop/real-estate-search-app/src/public are not separate folders.
```

# Storybookの設定

### preview.tsの設定

 以下のように`loaders: [mswLoader],`を追加します。

```ts:preview.ts
import type { Preview } from "@storybook/react";
import "../resources/css/app.css";
import { initialize, mswLoader } from 'msw-storybook-addon' // 追加

initialize()

const preview: Preview = {
    parameters: {
        controls: {
            matchers: {
                color: /(background|color)$/i,
                date: /Date$/i,
            },
        },
    },
    loaders: [mswLoader], // 追加
};

global.route = (name, params, absolute) => {
    return `/${name}`;
};

export default preview;
```

### vite.config.jsの修正

```ts:vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.tsx',
            refresh: true,
        }),
        react(),
    ],
    publicDir: 'public', // 追加
});

```

※これ設定しないと以下のようにエラーが出るので注意してください。

```
Error: [MSW] Failed to register a Service Worker for scope ('http://localhost:6007/') with script ('http://localhost:6007/mockServiceWorker.js'): Service Worker script does not exist at the given path.
  at async startWorkerInstance (/node_modules/.cache/storybook/dd4df79305cd50a532e8ccaa21ee67a8cbbc890739d67fa2856713328caf1b91/sb-vite/deps/msw-storybook-addon.js?v=0d345a9a:1202:24))
  at async SetupWorkerApi.start (/node_modules/.cache/storybook/dd4df79305cd50a532e8ccaa21ee67a8cbbc890739d67fa2856713328caf1b91/sb-vite/deps/msw-storybook-addon.js?v=0d345a9a:2709:12))
```

# テストの作成

実際にテストを書いてみます。

`/api/upload`というAPIを読んでいるコンポーネントがあったので、それをモックしてみようと思います。

以下のようになります。

```tsx:index.stories.tsx
import { Meta, StoryObj } from '@storybook/react';
import { UploadImageArea } from '.';
import { useState } from 'react';
import { HttpResponse, ResponseResolver, http } from 'msw';

const meta: Meta<typeof UploadImageArea> = {
    title: 'views/Admin/PropertyRegister/components/UploadImageArea',
    component: UploadImageArea,
    // 追加　⇩　⇩　⇩　⇩
    parameters: {
        msw: {
            handlers: [
                http.post('/api/upload', () => {
                    return HttpResponse.json({
                        test: 'Test',
                    })
                }),
            ],
        },
    },
    // 追加⇧⇧⇧⇧
    tags: ['autodocs'],
}
export default meta


type Story = StoryObj<typeof meta>

export const Test: Story = {
    render() {
        const [images, setImages] = useState<{ url: any; id: any; }[]>([]);
        return (
            <UploadImageArea
                images={images}
                onImageChange={setImages} />
        )
    },
}

```

これで`npm run storybook`コマンドを叩いて`test: 'Test'`というレスポンスが来ているか確認すると、しっかりと設定した値が返却されていることがわかります。

# おわりに

StorybookだけだとAPIのテストができないということで困ったこともあったのですが、mswを使用すると簡単にテストができました。

今後も積極的に使用していきたいと思います。

この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。

最後まで読んでいただきありがとうございました！

# 参考

https://mswjs.io/

https://storybook.js.org/addons/msw-storybook-addon