Mock Service Worker (MSW) を使ったAPIモックとテストの効率化
はじめに
本記事では、フロントエンド開発やテストにおいて非常に便利なツールである「Mock Service Worker (MSW)」について解説します。MSWを使用することで、バックエンドのAPIが未完成でもフロントエンドの開発を進めることができ、また、テストの安定性を向上させることができます。まずはMSWの基本的な使い方を紹介し、その後、MSWを使用しない場合のテストコードとどう違うかの比較を含めどのように活用できるかを詳しく説明していきます。
今回のゴール
データをフェッチするReactコンポーネントの自動テストを用いてMSWが有効である検証を行います。
最初はJestのモックを使って自動テストを行うコードを書きます。
その後、JestのモックをMSWに置き換えることでテストコードが簡単に書けるようになることを比較しながらご紹介します。
MSWサーバーの起動方法なども併せてご紹介しておりますので、この記事を参考に現場で実践できるようになれば幸いです。
Mock Service Worker(MSW)とは?
MSW(Mock Service Worker) は、API のモック(仮のサーバーレスポンスを作成) をするためのライブラリです。
ブラウザや Node.js 環境で動作し、フロントエンド開発やテストの際に 実際の API を呼び出さずにモックレスポンスを返せる のが特徴です。
主な用途
1.フロントエンド開発時にバックエンドが未完成でも作業できる
実際の API がまだ用意されていなくても、モックデータを返せるので開発が進められる。
API の設計変更があっても、すぐにモックデータを更新できる。
2.テストの安定化
Jest / Playwright / Cypress などのテストで API をモックすることで、外部 API の影響を受けずに安定したテストが可能。
ネットワークエラーや特定のレスポンスを簡単にシミュレーションできる。
3.ネットワークリクエストをフックし、開発中のデバッグを楽にする
MSW を使うと、実際にリクエストがどんなデータを送受信しているか確認しやすい。
例えば、API からのレスポンスをカスタマイズしてエラーハンドリングの動作をチェックできる。
記事一覧を表示するコンポーネントでテスト
今回はブログ記事の一覧を表示するコンポーネントを用意しテストコードの実装を検討してみます。
'use client'
import { useEffect, useState } from "react"
type Article = {
id: number,
title: string
description?: string
}
export const Articles = () => {
const [articles, setArticles] = useState<Article[] | null>(null)
useEffect(() => {
fetch('/api/articles')
.then((response) => response.json())
.then((data) => setArticles(data.message));
}, []);
return (
<div>
{articles?.length ? (
<ul>
{articles.map((item) =>
<li key={item.id}>{item.title}</li>
)}
</ul>
) : (
<p>記事がありません</p>
)}
</div>
)
}
画面を起動するとデータ取得の処理が動きます。
データがあった場合はuseState
でデータを格納しその結果を表示するというコンポーネントになります。
このコンポーネントを使って画面に表示する処理となります。
import { Articles } from "@/components/articles";
export default function Home() {
return (
<div>
<Articles />
</div>
)
}
この設定で先ほどの記事一覧のコンポーネントをブラウザで確認できますので一度、Next.jsを起動して確認します。
npm run dev
画面には「記事がありません」と出ておりコンソールログにはfetchで404 Not Foundのエラーが出ています。
テストコードの実装
まずはMock Service Workerを使わずにfetchをモックに置き換えて仮のデータでテストを実行します。
import { render, screen, waitFor } from '@testing-library/react';
import { Articles } from './articles';
describe('Articles Component', () => {
beforeEach(() => {
global.fetch = jest.fn();
});
afterEach(() => {
jest.resetAllMocks();
});
test('ブログ記事一覧を表示する', async () => {
const mockArticles = [
{ id: 1, title: '1件目の記事', description: 'これはテストです。' },
{ id: 2, title: '2件目の記事', description: 'これもテストです。' }
];
(global.fetch as jest.Mock).mockResolvedValueOnce({
json: jest.fn().mockResolvedValueOnce(mockArticles),
});
// コンポーネントをレンダリング
render(<Articles />);
expect(screen.getByText('記事がありません')).toBeInTheDocument();
await waitFor(() => {
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(screen.getByText('1件目の記事')).toBeInTheDocument();
expect(screen.getByText('2件目の記事')).toBeInTheDocument();
});
});
});
テストコードの解説になります。
beforeEach(() => {
global.fetch = jest.fn();
});
global.fetchはブラウザ環境におけるfetchのデフォルトのインスタンス。
jest.fn()でモック化して、mockResolvedValueOnceやmockRejectedValueOnceを使い、リクエストに応じたレスポンスを定義できます。
const mockArticles = [
{ id: 1, title: '1件目の記事', description: 'これはテストです。' },
{ id: 2, title: '2件目の記事', description: 'これもテストです。' }
];
(global.fetch as jest.Mock).mockResolvedValueOnce({
json: jest.fn().mockResolvedValueOnce(mockArticles),
});
mockResolvedValueOnce
を使い、json関数の戻り値として期待するデータを返します。
expect(screen.getByText('記事がありません')).toBeInTheDocument();
起動したタイミングでは記事データが存在しないため「記事がありません」と表示されることを確認します。
await waitFor(() => {
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(screen.getByText('1件目の記事')).toBeInTheDocument();
expect(screen.getByText('2件目の記事')).toBeInTheDocument();
});
記事データがセットされた後の状態を下記の2件で確認します。
li
タグが2つ存在すること- それぞれの記事タイトルが表示されること
テストコードができましたので実際にテストを実施します。
npm run test -- components/articles.test.tsx
Mock Service Workerを使わずともfetchをモックに置き換えることでコンポーネントのテストができました。
多くのケースではこれで十分かもしれません。
ただよりテストの実装をシンプルにし工数を抑えたいという方々向けにMock Service Worker(MSW)をご紹介したいと思います。
Mock Service Workerのセットアップ
msw
をプロジェクトにインストールします。
npm install msw --save-dev
MSWハンドラーを作成
APIリクエストのモックレスポンスを定義するハンドラーを作成します。
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/articles', () => {
return HttpResponse.json([
{ id: 1, title: '1件目の記事', description: 'これはテストです。' },
{ id: 2, title: '2件目の記事', description: 'これもテストです。' }
])
})
];
MSWサーバーを設定
テスト用のMSWサーバーを設定します。
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
JestセットアップでMSWを起動
テストが始まる前にMSWサーバーを起動し、終了時にクリーンアップします。
import '@testing-library/jest-dom'
+ import { server } from './mocks/server';
+ beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
+ afterEach(() => server.resetHandlers());
+ afterAll(() => server.close());
Jestの設定でjest.setup.tsを読み込むようにします。
※前回の記事と同じ環境でしたら既に設定してあるかと思うので対応不要です。
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
JestでNode.jsを動かすために
jest.config.tsでjestをブラウザ環境で動かすことを想定した設定を入れています。
const config: Config = {
clearMocks: true,
coverageProvider: "v8",
preset: "ts-jest",
testEnvironment: "jest-environment-jsdom",
そのため現状の設定でJestからMSWを起動するとエラーが発生します。
公式ドキュメントに従いjest-fixed-jsdom
を導入します。
npm i -D jest-fixed-jsdom
jest.config.ts
で設定変更します。
const config: Config = {
clearMocks: true,
coverageProvider: "v8",
preset: "ts-jest",
- testEnvironment: "jest-environment-jsdom",
+ testEnvironment: "jest-fixed-jsdom",
テストコードの修正
Jestでテストをする際にMSWが起動しているためfetchをそのままコンポーネント側で動かせるようになります。
fetchをモックに置き換えていた処理を削除します。
import { render, screen, waitFor } from '@testing-library/react';
import { Articles } from './articles';
describe('Articles Component', () => {
- beforeEach(() => {
- global.fetch = jest.fn();
- });
- afterEach(() => {
- jest.resetAllMocks();
- });
test('ブログ記事一覧を表示する', async () => {
- const mockArticles = [
- { id: 1, title: '1件目の記事', description: 'これはテストです。' },
- { id: 2, title: '2件目の記事', description: 'これもテストです。' }
- ];
- (global.fetch as jest.Mock).mockResolvedValueOnce({
- json: jest.fn().mockResolvedValueOnce(mockArticles),
- });
// コンポーネントをレンダリング
render(<Articles />);
expect(screen.getByText('記事がありません')).toBeInTheDocument();
await waitFor(() => {
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(screen.getByText('1件目の記事')).toBeInTheDocument();
expect(screen.getByText('2件目の記事')).toBeInTheDocument();
});
});
});
モックがなくなった分、見やすいテストコードになりました。
下記コマンドでテストを実行し正常終了することを確認します。
npm run test -- components/articles.test.tsx
さいごに
Mock Service Worker(MSW)は、フロントエンド開発やテストの効率化に非常に役立つツールです。実際のAPIリクエストをモックすることで、開発中に発生しがちな問題を軽減し、テストの信頼性を高めることができます。MSWを活用することで、バックエンドが完成していなくてもフロントエンドの開発が進められ、テストもスムーズに行うことが可能です。この記事を参考に、MSWをあなたのプロジェクトに取り入れて、開発効率をさらに向上させましょう。
関連する技術ブログ
Webアクセシビリティの完全ガイド:Lighthouse / axe による自動テスト、WCAG基準策定、キーボード操作・スクリーンリーダー対応まで
Webアクセシビリティの課題を解決するための包括的なガイド。Lighthouse / axe を活用した自動テストの設定、WCAGガイドラインに基づく評価基準の整備、キーボード操作やスクリーンリーダー対応の改善、カラーコントラストの最適化、ARIAランドマークの導入、フォームやモーダルの操作性向上まで詳しく解説。定期的なアクセシビリティレポートを活用し、継続的な改善を実現する方法も紹介します。
shinagawa-web.com
フロントエンド開発で欠かせないReactのUIコンポーネントのテストをReact Testing Libraryで実装
React Testing Libraryを使って、Reactコンポーネントのテストを行う方法を学びます。本記事では、Next.js環境でのセットアップから、ユーザーインタラクションをシミュレーションしたテストコードの作成までを詳しく解説します。コンポーネントが期待通りに動作するかを確認し、実際のアプリケーションに近い形でのテストを実装しましょう。
shinagawa-web.com
フロントエンドのテスト自動化戦略:Jest・Playwright・MSW を活用したユニット・E2E・API テスト最適化
フロントエンド開発において、品質を担保しながら効率的に開発を進めるためには、適切なテストの自動化が不可欠です。本記事では、Jest や Vitest を活用したユニットテストの導入・強化、React Testing Library や Storybook との統合によるコンポーネントテストの最適化、Playwright / Cypress を用いた E2E テストの拡充について詳しく解説します。さらに、Supertest や MSW を活用した API テストの自動化、Faker / GraphQL Mock によるモックデータの整理、CI/CD パイプラインにおける並列実行やキャッシュ活用による最適化など、テストを効果的に運用するための手法を紹介。また、Codecov / SonarQube によるテストカバレッジの可視化や、フィーチャーフラグを考慮したテスト戦略の策定についても解説し、実践的なアプローチを提案します。テストの信頼性と効率を向上させ、開発プロセスを強化したいフロントエンドエンジニア必見の内容です。
shinagawa-web.com
JestとTypeScriptで始めるテスト自動化:基本設定から型安全なテストの書き方まで徹底解説
JestとTypeScriptを使ったテスト自動化の基本を学びたい方へ。環境のセットアップ方法、型安全なテストを書くメリット、コードの信頼性を高める実践的なテクニックを初心者向けに丁寧に解説します。テストカバレッジの活用で、品質の高い開発を目指しましょう。
shinagawa-web.com
Supertest と Jest を活用した Express + MongoDB アプリのエンドツーエンドテスト解説
Supertest と Jest を組み合わせて Express アプリケーションの API テストを効率化する方法を解説します。サービス層の導入やテスト可能なコード設計へのリファクタリング、GET, POST, PATCH, DELETEメソッドのテスト実装まで、具体的なコード例を交えて詳しく紹介します。
shinagawa-web.com
JavaScript から TypeScript への移行完全ガイド:型安全性向上・バグ削減・開発効率を最大化
JavaScript から TypeScript への移行は、型安全性の向上やバグの削減に大きく貢献しますが、適切な計画なしに進めると、開発チームに負担がかかる可能性があります。本記事では、移行計画の策定から、コードベースの分析、移行対象ファイルの特定、tsconfig.json の最適化、型の設計と any の削減、段階的な移行戦略について詳しく解説します。さらに、リファクタリングのポイントや、TypeScript を活用したバグの早期発見、開発環境の最適化(IDE や CI ツールの設定)、チーム向けの TypeScript トレーニング、移行後のテスト強化まで、スムーズな移行を実現するためのベストプラクティスを紹介します。TypeScript への移行を成功させ、開発効率とコード品質を向上させたいエンジニア必見のガイドです。
shinagawa-web.com
ExpressとMongoDBで簡単にWeb APIを構築する方法【TypeScript対応】
本記事では、MongoDB Atlasを活用してREST APIを構築する方法を、初心者の方にも分かりやすいステップで解説します。プロジェクトの初期設定からMongoDBの接続設定、Expressを使用したルートの作成、さらにTypeScriptを用いた型安全な実装まで、実践的なサンプルコードを交えて丁寧に説明します。
shinagawa-web.com
Express(+TypeScript)入門ガイド: Webアプリケーションを素早く構築する方法
Node.jsでWebアプリケーションを構築するための軽量フレームワーク、Expressの基本的な使い方を解説。シンプルなサーバー設定からルーティング、ミドルウェアの活用方法、TypeScriptでの開発環境構築まで、実践的なコード例とともに学べます。
shinagawa-web.com
弊社の技術支援サービス
無駄なコストを削減し、投資対効果を最大化する
クラウド費用の高騰、不要なSaaSの乱立、開発工数の増加――これらの課題に悩んでいませんか?本サービスでは、クラウドコストの最適化、開発効率向上、技術選定の最適化 を通じて、単なるコスト削減ではなく、ROIを最大化する最適解 をご提案します。
shinagawa-web.com
最新技術の導入・検証を支援するPoCサービス
Remix、React Server Components、TypeScript移行、クラウドサービス比較、マイクロサービス、サーバーレス、デザインシステムなど、最新技術のPoC(概念実証)を通じて、最適な技術選定と導入を支援します。貴社の開発課題に合わせた検証・実装で、ビジネスの成長を加速させます。
shinagawa-web.com
開発生産性を最大化するための支援サービス
開発チームの生産性向上、コードの品質管理、インフラの最適化まで、様々な側面からサポートします。コードベースのリファクタリングから、テスト自動化、オンボーディング強化まで、プロジェクトの成功に必要なすべての支援を提供。御社の開発現場が効率的に機能するように、技術的な障害を取り除き、スムーズな開発を実現します。
shinagawa-web.com
開発品質向上支援 – 効率的で安定したプロダクトを実現
フロントエンドからバックエンド、データベースまで、開発プロセス全体を最適化し、安定したプロダクト作りをサポートします。コードレビューの仕組み、型定義の強化、E2Eテスト環境の構築など、開発の各ステップにおけるベストプラクティスを導入することで、より効率的でバグの少ない、そしてユーザー満足度の高いサービス提供を支援します。
shinagawa-web.com
Webアプリのセキュリティ強化支援
Webアプリの脆弱性対策からインフラのセキュリティ強化まで、包括的なセキュリティ支援を提供。OWASP Top 10対策、JWT認証の最適化、APIのアクセス制御、依存パッケージの監査、セキュアコーディングの標準化など、実践的なアプローチで開発現場の安全性を向上させます。
shinagawa-web.com
目次
お問い合わせ