見出し画像

Vite+React環境にMSWを導入してみる


はじめに


こんにちは、株式会社SHIFT ITソリューション部の渡部です。

React、TypeScriptを使用した SPA開発をしています。

開発を進めていく中でAPIのモックが必要になり、Mock Service Worker (以下「MSW」)を導入しました。今回はMSWの導入方法とREST APIのモックの書き方をまとめました。

環境


  • Vite : 4.3.9

  • React : 18.2.8

  • TypeScript : 5.1.3

  • MockServiceWorker : 1.2.2

導入


前提として、Reactプロジェクトがすでに作成されているものとします。

インストール


まず、下記コマンドでMSWをインストールします。

npm install msw --save-dev 

ファイルの準備


mockディレクトリと以下のファイルをそれぞれ新規作成します。

mkdir src/mocks
mocks 
    ├─ browser.ts
    ├─ handlers.ts
    └─ server.ts

browser.ts

import { setupWorker } from 'msw';
import { restHandlers } from './handlers';

export const worker = setupWorker(...restHandlers);

server.ts

import { setupServer } from 'msw/node';
import { restHandlers } from './handlers';

export const server = setupServer(...restHandlers);

setupTests.ts

Jestでテストを行っている方はsetupTests.tsに以下追記してください。

import '@testing-library/jest-dom';
import { server } from './src/mocks/server';

beforeAll(() => {
  server.listen();
});

afterEach(() => {
  server.resetHandlers();
});

afterAll(() => {
  server.close();
});

main.tsx

ここで、ローカルで開発する時のみMSWを使用するように設定します。

//以下追記
if (import.meta.env.MODE === 'dev') {
    const { worker } = await import('./mocks/browser');
    worker.start();
}

実装


handlers.ts

モックを記述していくファイルになります。 実際に使用する想定のAPIのパス、レスポンスを記述してください。

クエリパラメータがある場合や、リダイレクトする場合、リクエストボディの値によってレスポンスが変化する場合、などサンプルコードに記述しています。ユースケースに合わせてご参考ください。

import { rest } from 'msw';

export const restHandlers = [
  // getメソッド
    rest.get('/drink', (_req, res, ctx) => {
        return res(
            ctx.status(200),
            ctx.json([
                {
                    "name": "烏龍茶",
                    "code": "01",
                },
                {
                    "name": "コーヒー",
                    "code": "02",
                }
            ]),
        );
    }),
    // クエリパラメータがある場合(/manu?type=foods)
    rest.get('/menu', (req, res, ctx) => {
        const query = req.url.searchParams;
        const type = query.get('type');
        if (type === 'foods') {
            return res(
            ctx.status(200),
            ctx.json([
                {
                    "name": "サンドイッチ",
                    "code": "01",
                },
                {
                    "name": "ハンバーガー",
                    "code": "02",
                },
            ]),
        );
        } else if (type === 'sweets') {
            return res(
            ctx.status(200),
            ctx.json([
                {
                    "name": "プリン",
                    "code": "10",
                },
            ]),
        );
        }
    }),
    // postメソッド
    // リクエストに応じてレスポンスを分けたい場合
    rest.post('/order', async (req, res, ctx) => {
        const { code } = await req.json()
        // リクエストボディに"code"というkeyがあり、そのvalueによってレスポンスが変わる
        if (code === '10') {
            return res(
                ctx.status(400),
                ctx.json({
                    errorMessage: 'プリンは現在品切れです。',
                }),
            );
        }
        // リダイレクトする場合
        else if (code === 'X') {
            return res(
              ctx.status(302),
              ctx.set('Location', 'リダイレクト先'),
            );
        }
        return res(
            ctx.status(200),
            ctx.json({
                message: "注文を承りました。"
            }),
        );
    }),
]

実装ファイルでは、fetchやaxiosなどを使用しパスを指定して呼び出すだけです。 import文も何も実装ファイルには記述する必要ありません。

単体テスト(Jest)


実装ファイルと同じく、import文など記述しなくてもhandler.tsに設定したレスポンスが返却されます。

しかし、エラー時の挙動をテストしたい、など特定のテストでのみレスポンスを変更したい場合もあると思います。 その場合は、テストコードに以下のような記述をするとテストコード内でモックのレスポンスを変更できます。

import Sample from '../sample';
import { rest } from 'msw';
import { server } from '../mocks/server';

describe('異常系', () => {
    beforeAll(() => server.listen());
    afterEach(() => server.resetHandlers());
    afterAll(() => server.close());
    
    test('/drink でエラー発生', async () => {
        // /drinkのレスポンスを書き換える
        server.use(
            rest.get('/drink', (_req, res, ctx) => {
            return res(
                ctx.status(404),
                ctx.json({
                    errorCode: 'E10000',
                }),
            );
            }),
        );
        render(<Sample />);
        await waitFor(() =>
            expect(
                screen.getByText('E10000'),
            ).toBeInTheDocument(),
        );
    });
});

おわりに


MSWの導入方法、使い方について紹介させていただきました。

フロントエンド開発をする際にAPIのモックを作ることができると開発にとても便利ですし、単体テストのカバレッジを上げるにも必須になってきます。実際のAPIを呼び出す時もスムーズに行えるので、是非導入してみてください。

Reactについて他にもいくつか記事を書いているので、こちらもご参考になれば幸いです。


執筆者プロフィール : 渡部 瑠菜
2022年4月に新卒入社後、ITソリューション部に所属。
AWSやReactを勉強中。
趣味は絵を描くことや2D/3Dモデリングなど。

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/

PHOTO:UnsplashAngelo Pantazis