logo

【APIモック編】React Application Architecture for Production が勉強になりすぎる

2023-06-14
a year ago

開発環境

  • react 18.2.0
  • next 13.4.2
  • msw 1.2.1
  • msw-devtools 1.0.2
  • @mswjs/data 0.12.0

前提

今回の記事はReactのベストプラクティスと呼び声の高いbulletproof-reactで有名なAlan氏が2023年1月に公開した著書「React Application Architecture for Production」の紹介です。

ソースコードは公開されているので、こちらを眺めるだけでも学べることはたくさんあると思いますが、個人的に気になる部分を本記事含めいくつかの記事に分けて紹介します。

※上記で記した開発環境のバージョンは実際に動作確認した際に利用したバージョンです。著者で紹介されているバージョンと異なりますのでご了承ください。

本題

今回はAPIモックについて簡単な概要とDevToolsのセットアップ方法を紹介します。

著書ではMSWを利用しています。

概要

MSWはブラウザからのリクエストをService Workerがインターセプトし、任意のレスポンスを返すことが出来るライブラリです。

優れている点として2点あります。

  • Node プロセス(サーバー)でも動作するのでSSRも対応可能
  • ネットワークレベルでインターセプトするのでブラウザ開発ツールの【ネットワーク】からリクエストの詳細を確認できる

ネットワークレベルでインターセプトするので、ネットワークエラーを簡単に再現できます。

開発中はネットワークエラーを再現しにくいこともあるので、個人的には使いやすいポイントです。

DevToolsのセットアップ

MSWと併せて利用すると便利なmsw-devtoolsというライブラリの紹介です。

利用するメリットは以下3つです。

  • ブラウザ上からmock可能なAPIの一覧を確認できる
  • ブラウザ上から簡単にリクエストを実行できる(リクエストヘッダも設定可能)
  • テストデータをブラウザ上で確認できる

セットアップ方法はとても簡単で以下のステップで導入できます。

①Wrapperを実装してプロジェクト全体をラップする

import { MSWDevTools } from 'msw-devtools';
import type { ReactNode } from 'react';

import { db, handlers } from '@/testing/mocks';

export type MSWWrapperProps = {
  children: ReactNode;
};

require('@/testing/mocks/initialize');

export const MSWWrapper = ({
  children,
}: MSWWrapperProps) => {
  return (
    <>
      {process.env.NODE_ENV === 'development' && (
        <MSWDevTools db={db} handlers={handlers} />
      )}
      {children}
    </>
  );
};
src/lib/msw.tsx

※開発環境でのみ有効にします。


import { NextPage } from 'next';
import type { AppProps } from 'next/app';
import dynamic from 'next/dynamic';
import type { ReactElement, ReactNode } from 'react';

import { API_MOCKING } from '@/config/constants';
import { MSWWrapperProps } from '@/lib/msw';
import { AppProvider } from '@/providers/app';

type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

const MSWWrapper = dynamic<MSWWrapperProps>(() =>
  import('@/lib/msw').then(({ MSWWrapper }) => MSWWrapper)
);

const App = ({
  Component,
  pageProps,
}: AppPropsWithLayout) => {
  const getLayout =
    Component.getLayout ?? ((page) => page);

  const pageContent = getLayout(
    <Component {...pageProps} />
  );

  return (
    <AppProvider>
      {API_MOCKING ? (
        <MSWWrapper>{pageContent}</MSWWrapper>
      ) : (
        pageContent
      )}
    </AppProvider>
  );
};

export default App;
_app.tsx

※APIモックを利用する必要がない場合は(この例ではAPI_MOCKINGを)フラグを切り替えています。

②dbを設定

各APIで利用するデータベースを作成します。

別途ライブラリが必要なのでインストールします。

mswjs/dataはバックエンドで利用するORMと同じように利用できます。

こちらを利用せず、テストコードの中でレスポンスをハードコードする方法も可能ですが、開発効率をできるだけ高めるためにもmswjs/dataを利用する価値はあります。

メモリ内にデータベースを構築し、以下のような記述でデータにアクセスできます。

db.user.findFirst

モデルを定義する必要があるので定義します。

import { factory, primaryKey } from '@mswjs/data';
import { v4 as uuidv4 } from 'uuid'

const models = {
  user: {
    id: primaryKey(uuidv4()),
    createdAt: Date.now,
    email: String,
    password: String,
  },
	...(省略)
};

export const db = factory(models);
src/testing/mocks/db.ts

③handlerを設定

import { rest } from 'msw';

export const handlers = [
  rest.get('http://localhost:3000/healthcheck', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ healthy: true })
    );
  }),
];
src/testing/mocks/index.ts

restを利用してモックするAPIを定義していきます。

この例では簡単なヘルスチェックのリクエストのみ定義していますが、必要なテストをhandlers配列に追加していくことになります。

さいごに

MSWを利用する上で便利なライブラリとその最低限のセットアップ例を紹介しました。

本記事では紹介していませんが、よりテストを充実させるには初期データのシードなども必要になってきます。

詳しい内容はReact Application Architecture for Productionで紹介されていますので、興味のある方はぜひ購入を検討してみてください。

(公開されているソースコードを見るだけでもキャッチアップできると思います)

参照