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について他にもいくつか記事を書いているので、こちらもご参考になれば幸いです。
初めてのReactでログイン画面を作ってみた https://note.shiftinc.jp/n/nbc80e4141ffd
React useContextでグローバルstateを扱う https://note.shiftinc.jp/n/nf3c0a2563d89
React Router dom useLocationをテストする https://note.shiftinc.jp/n/n4c4d7b68fa5e
お問合せはお気軽に
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:UnsplashのAngelo Pantazis