Tips & Tricks

Claude Code के साथ MSW API

Claude Code का उपयोग करके msw api सीखें। Practical tips और code examples शामिल हैं।

MSWでリアルなAPIモック環境 buildする

MSW(Mock Service Worker)はService Workerを利用してnetworkレベルでAPIrequestをインターセプトするモックlibrary है।testとブラウザdevelopmentの両方でsameモックhandlerを共有でき、実際のHTTPrequestとsame振る舞いを再現し है।Claude Codeはhandlerの設計 से型safeなモックbuild तक的確にサポートし है।

basic handler設計

Claude Codeにhandlerの構成を依頼 करें।

> MSWでuserAPIのモックhandlerを設計して。
> CRUD操作、error handling、response遅延を含めて。
import { http, HttpResponse, delay } from "msw";

// インメモリdatabase
let users: User[] = [
  { id: "1", name: "Alice", email: "[email protected]", role: "admin" },
  { id: "2", name: "Bob", email: "[email protected]", role: "editor" },
];

export const userHandlers = [
  // listfetch
  http.get("/api/users", async ({ request }) => {
    await delay(100);
    const url = new URL(request.url);
    const page = Number(url.searchParams.get("page") || "1");
    const perPage = Number(url.searchParams.get("perPage") || "20");
    const search = url.searchParams.get("search") || "";

    let filtered = users;
    if (search) {
      filtered = users.filter(
        (u) =>
          u.name.toLowerCase().includes(search.toLowerCase()) ||
          u.email.toLowerCase().includes(search.toLowerCase())
      );
    }

    const start = (page - 1) * perPage;
    const paginated = filtered.slice(start, start + perPage);

    return HttpResponse.json({
      data: paginated,
      meta: { total: filtered.length, page, perPage },
    });
  }),

  // 詳細fetch
  http.get("/api/users/:id", async ({ params }) => {
    await delay(50);
    const user = users.find((u) => u.id === params.id);
    if (!user) {
      return HttpResponse.json(
        { error: "userが見つかりません" },
        { status: 404 }
      );
    }
    return HttpResponse.json({ data: user });
  }),

  // create
  http.post("/api/users", async ({ request }) => {
    await delay(200);
    const body = (await request.json()) as Omit<User, "id">;
    const newUser: User = { ...body, id: String(Date.now()) };
    users.push(newUser);
    return HttpResponse.json({ data: newUser }, { status: 201 });
  }),

  // update
  http.put("/api/users/:id", async ({ params, request }) => {
    await delay(150);
    const body = (await request.json()) as Partial<User>;
    const index = users.findIndex((u) => u.id === params.id);
    if (index === -1) {
      return HttpResponse.json(
        { error: "userが見つかりません" },
        { status: 404 }
      );
    }
    users[index] = { ...users[index], ...body };
    return HttpResponse.json({ data: users[index] });
  }),

  // delete
  http.delete("/api/users/:id", async ({ params }) => {
    await delay(100);
    users = users.filter((u) => u.id !== params.id);
    return new HttpResponse(null, { status: 204 });
  }),
];

testでのintegration

Vitestとのintegrationpattern है।

import { setupServer } from "msw/node";
import { userHandlers } from "./handlers/user";

// test用server
const server = setupServer(...userHandlers);

beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe("UserList component", () => {
  it("userlist displayする", async () => {
    render(<UserList />);

    await waitFor(() => {
      expect(screen.getByText("Alice")).toBeInTheDocument();
      expect(screen.getByText("Bob")).toBeInTheDocument();
    });
  });

  it("APIerror時にerrormessage displayする", async () => {
    // test単位でhandlerをऊपर書き
    server.use(
      http.get("/api/users", () => {
        return HttpResponse.json(
          { error: "servererror" },
          { status: 500 }
        );
      })
    );

    render(<UserList />);

    await waitFor(() => {
      expect(screen.getByText("dataのfetchに失敗しました")).toBeInTheDocument();
    });
  });

  it("networkerror時にリトライbuttonがdisplayされる", async () => {
    server.use(
      http.get("/api/users", () => {
        return HttpResponse.error();
      })
    );

    render(<UserList />);

    await waitFor(() => {
      expect(screen.getByRole("button", { name: "再試行" })).toBeInTheDocument();
    });
  });
});

ブラウザでのdevelopment用モック

ブラウザ環境でService Worker के रूप में動作させるpattern है।

// src/mocks/browser.ts
import { setupWorker } from "msw/browser";
import { userHandlers } from "./handlers/user";
import { authHandlers } from "./handlers/auth";

export const worker = setupWorker(...userHandlers, ...authHandlers);

// src/main.tsx
async function enableMocking() {
  if (import.meta.env.DEV) {
    const { worker } = await import("./mocks/browser");
    return worker.start({
      onUnhandledRequest: "bypass",
    });
  }
}

enableMocking().then(() => {
  ReactDOM.createRoot(document.getElementById("root")!).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
});

型safeなhandler設計

TypeScriptの型とMSWhandlerをintegrationするpattern है।

import { http, HttpResponse } from "msw";

// APItype definitions
interface ApiEndpoints {
  "GET /api/users": {
    response: { data: User[]; meta: PaginationMeta };
    params: never;
  };
  "GET /api/users/:id": {
    response: { data: User };
    params: { id: string };
  };
  "POST /api/users": {
    response: { data: User };
    body: CreateUserInput;
    params: never;
  };
}

// factoryーfunction
function createUserFactory(overrides: Partial<User> = {}): User {
  return {
    id: String(Math.random()).slice(2, 10),
    name: `User ${Math.random().toString(36).slice(2, 6)}`,
    email: `user-${Date.now()}@example.com`,
    role: "viewer",
    ...overrides,
  };
}

// testシナリオ用のhandlerセット
export const emptyStateHandlers = [
  http.get("/api/users", () =>
    HttpResponse.json({ data: [], meta: { total: 0, page: 1, perPage: 20 } })
  ),
];

export const errorHandlers = [
  http.get("/api/users", () =>
    HttpResponse.json({ error: "Internal Server Error" }, { status: 500 })
  ),
];

export const slowResponseHandlers = [
  http.get("/api/users", async () => {
    await delay(5000);
    return HttpResponse.json({ data: [createUserFactory()], meta: { total: 1, page: 1, perPage: 20 } });
  }),
];

Summary

MSWはnetworkレベルのモック से、実際のAPI通信に近い環境でtestとdevelopmentができ है।Claude Code का लाभ उठाकर、handler設計、testintegration、型safeな構成をकम समय मेंbuildpossible है।

ユニットtestとのintegrationはVitestऊपर級テクニックを、E2EtestでのutilizationはPlaywright E2Etest実践ガイドをदेखें。MSWofficial documentationもconfirmしておきましょう。

#Claude Code #MSW #APIモック #testing #frontend