Tips & Tricks

MSW API:Claude Code 实战指南

了解msw api:Claude Code 实战. 包含实用技巧和代码示例。

MSWでリアルなAPIMock環境を构建する

MSW(Mock Service Worker)はService Workerを利用して网络レベルでAPI请求をインターセプトするMock库です。测试と浏览器开发の両方で同じMock处理程序ーを共有でき、実際のHTTP请求と同じ振る舞いを再現します。Claude Code 处理程序ーの设计から类型安全なMock构建まで的確にサポートします。

基本的处理程序ー设计

让 Claude Code处理程序ーの结构を依頼吧。

> MSWで用户APIのMock处理程序ーを设计して。
> CRUD操作、错误处理、响应遅延を含めて。
import { http, HttpResponse, delay } from "msw";

// イン内存数据库
let users: User[] = [
  { id: "1", name: "Alice", email: "[email protected]", role: "admin" },
  { id: "2", name: "Bob", email: "[email protected]", role: "editor" },
];

export const userHandlers = [
  // 列表获取
  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 },
    });
  }),

  // 详情获取
  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: "ユーザーが見つかりません" },
        { status: 404 }
      );
    }
    return HttpResponse.json({ data: user });
  }),

  // 创建
  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 });
  }),

  // 更新
  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: "ユーザーが見つかりません" },
        { status: 404 }
      );
    }
    users[index] = { ...users[index], ...body };
    return HttpResponse.json({ data: users[index] });
  }),

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

测试での集成

Vitestとの集成パターンです。

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

// 测试用服务器
const server = setupServer(...userHandlers);

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

describe("UserList コンポーネント", () => {
  it("ユーザー一覧を表示する", async () => {
    render(<UserList />);

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

  it("APIエラー時にエラーメッセージを表示する", async () => {
    // 测试単位で处理程序ーを上書き
    server.use(
      http.get("/api/users", () => {
        return HttpResponse.json(
          { error: "サーバーエラー" },
          { status: 500 }
        );
      })
    );

    render(<UserList />);

    await waitFor(() => {
      expect(screen.getByText("データの取得に失敗しました")).toBeInTheDocument();
    });
  });

  it("ネットワークエラー時にリトライボタンが表示される", async () => {
    server.use(
      http.get("/api/users", () => {
        return HttpResponse.error();
      })
    );

    render(<UserList />);

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

浏览器での开发用Mock

浏览器環境でService Worker作为動作させるパターンです。

// 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>
  );
});

类型安全な处理程序ー设计

TypeScriptの类型とMSW处理程序ーを集成するパターンです。

import { http, HttpResponse } from "msw";

// API类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类类型定義
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;
  };
}

// ファクトリー函数
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,
  };
}

// 测试シナリオ用の处理程序ーセット
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 } });
  }),
];

总结

MSWは网络レベルのMockにより、実際のAPI通信に近い環境で测试と开发が可以。Claude Codeを活用すれば、处理程序ー设计、测试集成、类型安全な结构を短时间で构建是可能的。

单元测试との联动はVitest上級テクニックを、E2E 测试での活用はPlaywright E2E 测试実践指南MSW官方文档也建议确认一下。

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