🪃

【Jest + Testing Library + MSW】再フェッチ挙動の再現

2024/11/29に公開

ちょっとしたことですが「どうやって実装すればいいんだっけ?」となったので

はじめに

何らかの処理を行った後にデータを再フェッチするページのコンポーネントがあるとして、そのコンポーネントをテストする際に、処理後の再フェッチの挙動を再現したい場合があります。
本記事では リクエスト回数に応じてAPIモックのレスポンスを変更し、再フェッチの挙動を再現する方法 を紹介します。

テストしたい内容

例えば何らかの設定を行うフォームがあったとして、初期状態では「未設定」のバッジが表示されているとします。

フォームを入力後送信ボタンがクリックされるとAPIリクエストが発生して入力内容が保存され、その後データが再フェッチされると「設定済」状態のデータが返され、そのレスポンスに応じてバッジの表示も変更される仕様です。

だいたいこんな実装のものがあるイメージです
type BadgeProps = {
    status: ConfigStatus
};
export const Badge = ({ status }: BadgeProps) => (
    <div>
        <p>{status === 'configured' ? '設定済' : '未設定'}</p>
    </div>
);

export const Page = () => {
    const { data, refetch } = useFetchData();
    const { mutate } = useMutateData();

    const onSubmitHandler = async (formData) => {
        try{
            await mutate(format(formData));
            await refetch();
        } catch(err) {
            // エラーハンドリング
        }
    }

    return (
        <div>
            {data && <Badge status={data.status} />}
            <form onSubmit={onSubmitHandler}>
                {/* 以下入力フォームがある */}
            </form>
        </div>
    );
};

テストの実装

テストで再現したい挙動は

  • 初回リクエストでは設定が不完全な状態を返却する
  • ボタン操作後の再フェッチでは、正しい設定情報を返却する

というものです。つまりデータフェッチが初回なのか2回目なのかを判定し、それに応じてレスポンスを変更できればいいということです。
リクエストの回数がカウントできればいいので、実装自体はかなりシンプルなものになりました。

実装例

// 各種importやモックサーバーのセットアップなどは省略
// ある程度簡略化しています

describe('Page Component', () => {
  it('フォーム送信と再フェッチが正しく動作する', async () => {
    let requestCount = 0;

    server.use(
      rest.get('/api/settings/1', (req, res, ctx) => {
        requestCount += 1;
    
        // 初回リクエストと再フェッチでレスポンスを変更
        if (requestCount === 1) {
          return res(
            ctx.status(200),
            ctx.json({
              config_status: 'not_configured',
            })
          );
        }
    
        // 再フェッチ後のレスポンス
        return res(
          ctx.status(200),
          ctx.json({
            config_status: 'configured',
          })
        );
      })
    );

    render(<Page />);

    // 初回データ取得を待機
    await waitFor(() => {
      expect(/* 初回データ取得後に表示される何らかの要素 */).toBeInTheDocument();
    });

    // バッジが未設定
    expect(await screen.findByText('未設定')).toBeInTheDocument();

    const submitButton = screen.getByRole('button', { name: '送信' });
    await userEvent.click(submitButton);

    // 更新後の状態が反映されているか確認
    expect(await screen.findByText('設定済')).toBeInTheDocument();

    // APIリクエストが2度行われているか(=再フェッチの挙動が正しいか)確認
    expect(requestCount)toBe(2);
  });
});

Discussion