LoginSignup
2
1

Mock Service Worker でフロントエンド自動テストのREST APIコールをモックする

Last updated at Posted at 2023-04-18

はじめに

この記事では、Mock Service Worker というAPIをモックするライブラリを使って、フロントエンド自動テストのREST APIコールをモックする手順について記載していきます。

前提

開発環境は以下の通りです。

  • Windows11
  • VSCode
  • TypeScript 5.0.2
  • Node.js 18.15.5
  • Express 4.18.2
  • React 18.2.0
  • Vite 4.2.0
  • Vitest 0.29.8
  • @testing-library/react 14.0.0

事前にテスト対象となるToDoアプリを作成しています。
概要は以下の通りです。

Backend

Backend側では、ToDoリストを持っています。

backend/server.ts
import express, { Express, Request, Response } from "express";
import { Todo } from "../types";
import cors from "cors";

const app: Express = express();
const port = 3001;

app.use(cors());

const sampleData = [
  { id: 1, title: "牛乳を買う", completed: false },
  { id: 2, title: "洗濯をする", completed: true },
  { id: 3, title: "請求書を支払う", completed: false },
];

let todoList: Todo[] = sampleData;

app.get("/todo-list", (req: Request, res: Response) => {
  res.json(todoList);
});

app.listen(port, () => {
  console.log(`listening on port ${port}`);
});

Frontend

Frontend側で上記のToDoリストを取得します。

frontend/src/api/index.ts でAPIをコールするフックを定義します。

frontend/src/api/index.ts
import useSWR from "swr";
import { Todo } from "../../../types/";

export const fetcher = async (path: string) => {
  try {
    const res = await fetch("http://localhost:3001" + path);
    return res.json();
  } catch (error) {
    console.error(error);
  }
};

export const useTodoList = () => useSWR<Todo[]>("/todo-list", fetcher);

そして、frontend/src/routes/Home.tsx で上記のフックを呼び出し、取得したToDoリストを表示します。

frontend/src/routes/Home.tsx
import { FC } from "react";
import { useTodoList } from "../api";

export const Home: FC = () => {
  const { data: todoList, error, isLoading } = useTodoList();

  if (error) return <div>failed to load</div>;
  if (isLoading) return <div>loading...</div>;

  return (
    <div>
      <h1>ToDoリスト</h1>
      <ul>
        {todoList?.map((todo) => (
          <li key={todo.id}>
            <input
              type="checkbox"
              id={String(todo.id)}
              checked={todo.completed}
            />
            <label htmlFor={String(todo.id)}>{todo.title}</label>
          </li>
        ))}
      </ul>
    </div>
  );
};

image.png

Frontendの自動テスト

上記のFrontendの自動テストは以下のように実装しています。
この時点では、Home コンポーネントをレンダリングし、見出しが表示されていることをテストしています。

__tests__/integration/routes/Home.test.tsx
import { render, screen, waitFor } from "@testing-library/react";
import { Home } from "../../../routes/Home";

describe("Home Page test", () => {
  test("Render Home", async () => {
    render(<Home />);

    await waitFor(() => {
      screen.getByRole("heading", { name: "ToDoリスト" });
      screen.debug();
    });
  });
});

しかし、このテストを実行すると、テスト結果は成功となるものの、BackendのREST APIが動作しないため、フェッチエラーが発生してしまいます。
image.png
そのため、Mock Service Worker でREST APIのモックを作成します。

1. Mock Service Worker インストール

以下のコマンドでMock Service Worker をインストールします。

yarn add msw --dev

2. モックAPIの定義

まずはモックAPI定義用のファイル src/__tests__/libs/mocks/handlers.ts を作成します。
次に msw(Mock Service Worker)から rest をインポートし、モックを定義します。

src/__tests__/libs/mocks/handlers.ts
import { rest } from "msw";

export const DUMMY_TODO_LIST = [
  { id: 1, title: "牛乳を買う", completed: false },
  { id: 2, title: "洗濯をする", completed: true },
  { id: 3, title: "請求書を支払う", completed: false },
];

export const handlers = [
  rest.get("http://localhost:3001/todo-list", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(DUMMY_TODO_LIST));
  }),
];

"http://localhost:3001/todo-list" リクエストに対し、ToDoリストのデータを返すようにしています。レスポンスの引数はそれぞれ以下の通りです。

  • req:リクエストに関する情報(今回は利用していません)
  • res:モックレスポンスを作成するユーティリティ
  • ctx:モックレスポンス内のステータスコードやヘッダー、ボディなどを設定する機能群
    これでモックの定義はできました。

3. 利用設定

先ほど定義したモックAPIを自動テストで利用できるようにします。
まずは、モックサーバー設定用のファイル src/__tests__/mocks/server.ts を作成します。このファイル内でサーバーインスタンスを作成し、先ほど定義したモックを読み込みます。

src/__tests__/libs/mocks/server.ts
import { setupServer } from "msw/node";
import { handlers } from "./handler";

export const server = setupServer(...handlers);

次にモックサーバー設定用のファイルを自動テスト設定用ファイルで読み込みます。

setup.ts
import matchers from "@testing-library/jest-dom/matchers";
import { cleanup } from "@testing-library/react";
import { afterEach, expect } from "vitest";
import { server } from "./mocks/server";

// extends Vitest's expect method with methods from react-testing-library
expect.extend(matchers);

// establish API mocking before all tests
beforeAll(() => server.listen());

afterEach(() => {
  // reset any request handlers that we may add during the tests,
  // so they don't affect other tests
  server.resetHandlers();
  // runs a cleanup after each test case (e.g. clearing jsdom)
  cleanup();
});

// clean up after the tests are finished
afterAll(() => server.close());

4. テスト実行

これでテストを実行しても先ほどのフェッチエラーは発生しなくなりました。

image.png

また、APIレスポンスをモックしたので、レスポンス内容のテストも追加できます。

__tests__/integration/routes/Home.test.tsx
import "@testing-library/jest-dom";
import { render, screen, waitFor } from "@testing-library/react";
import { Home } from "../../../routes/Home";
import { DUMMY_TODO_LIST } from "../../libs/dummyData";

describe("Home Page test", () => {
  test("Render Home", async () => {
    render(<Home />);

    await waitFor(() => {
      screen.getByRole("heading", { name: "ToDoリスト" });

      const todoList = screen.getAllByRole("listitem");
      expect(todoList).toHaveLength(DUMMY_TODO_LIST.length);
      todoList.forEach((todo, index) => {
        const DUMMY_TODO = DUMMY_TODO_LIST[index];
        expect(todo).toHaveTextContent(DUMMY_TODO.title);
      });
      screen.debug();
    });
  });
});

最後に

今回は Mock Service Worker を使って、フロントエンドテストのREST APIコールをモックする手順について記載しました。Mocke Service Worker は他にもたくさん機能があるので、今後扱っていければと思っています。

参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1