mswのすゝめ #techtekt Advent Calendar 2022

はじめに

この記事は techtekt アドベントカレンダー2022 の14日目の記事です🎄✨

どうも!エンジニアリング統括部 サービス開発部でエンジニアをしています佐藤ゆです。
フロントエンド開発の際、APIのモック化を行う必要がありますよね。
その際のモック化するにあたり、皆さんは何のライブラリを用いていますか?
今日は私が担当したNuxt.jsのプロジェクトで導入したmswについて簡単に紹介したいと思います。

mswとは

mswjs.io

API mocking of the next generation
Mock by intercepting requests on the network level. Seamlessly reuse the same mock definition for testing, development, and debugging.

翻訳すると

次世代型APIモッキング
ネットワークレベルでリクエストをインターセプトしてモック化する。
テスト、開発、デバッグのために同じモック定義をシームレスに再利用することができます。

と記載されている通り、ローカル開発・テスト・デバッグの際に簡単に利用することができる優れものです。

Nuxt.js、Next.jsなど、最近よく使われているであろうFWでももちろん使えます。

特徴

mswの特徴は、ネットワークレベルでモック化するという点です。

mswフロー図
流用元: Introduction - Mock Service Worker Docs

mswを使用するとブラウザにServiceWorkerが登録され、リクエストを傍受します。

傍受したリクエストを読み取り、登録されているハンドラのエンドポイントのレスポンスをServiceWokerが返すという仕組みになっています。
Chromeであればdev toolからリクエストとレスポンスの確認を簡単に行うことができます。
また、モックサーバーを別で立てたりする必要がありません。
動作確認をする際にもストレスフリーです。

サンプル

例えば、以下のコードだと/userに対しリクエストを投げると定義した値が返ってくるようになります。

import {
  rest,
  MockedRequest,
  ResponseResolver,
  restContext,
  setupWorker,
  SetupWorkerApi,
} from 'msw';
import { setupServer, SetupServerApi } from 'msw/node';

interface IUser {
  id: number;
  name: string;
}

export const getUser: ResponseResolver<
  MockedRequest,
  typeof restContext,
  IUser
> = (_, res, ctx) => {
  return res(
    ctx.json({
      id: 1,
      name: 'さとうゆ@ジロリアン',
    })
  );
};

export const handlers = [
  rest.get('/user', getUser),
]

// Browser
export const worker: SetupWorkerApi = setupWorker(...handlers);

// Server
export const server: SetupServerApi = setupServer(...handlers);
// ブラウザの場合
if (process.env.NODE_ENV === 'development') {
  await worker.start();
}
// jestの場合
import { server } from 'どこか';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

※本来はそれぞれハンドラ定義、モックデータ定義などを別ファイルに記載する方が良いですが、今回は紹介ということで一つのファイルにまとめています。

クエリによって返す値を変更したいという動的なケースもあると思いますが、それも勿論可能です。

export const getUser: ResponseResolver<
  MockedRequest,
  typeof restContext,
  User
> = (req, res, ctx) => {
  if (req.url.searchParams.get('id') === '2') {
    return ctx.json({
      id: 2,
      name: '佐藤ゆ@ジロリアン',
    })
  }
  return res(
    ctx.json({
      id: 1,
      name: 'さとうゆ@ジロリアン',
    })
  );
};

これらのコードを記述するだけでローカル開発の際はモックを意識せずに開発することができるようになります。

また、先ほどのコードの// jestの場合部分のコードによりnode上でも動作するようにサポートされているため、jestなどを用いたテスト実行時も定義したデータをレスポンスしてくれます。
そのため、テスト用に別のモックを定義するといったことが必要なくなります!

まとめ

既存プロジェクトへの導入は難しいかもしれませんが、新規プロジェクトであれば導入検討の余地はあるかもしれません。
モック化をどうしようかなと思っている方がいましたらこの機会にぜひ触ってみてください!

エンジニアリング統括部 サービス開発部 第4グループ satoの写真

佐藤ゆさんのプロフィール

エンジニアリング統括部 サービス開発部 第4グループ エンジニア
趣味は音楽、ゲーム、アニメ、卓球など。音楽は邦ロック、ゲームはApexや鉄拳が好き。 ラーメンが大好きでラーメン二郎は小1から。

※2022年12月現在の情報です。