🎪
jest における MSW の活用事例
MSW を使った jest のテストについて、引数などの検査が面倒という記事を拝見したので、もし同様にアプローチを模索されている方がいらっしゃれば参考に、と思い記事にしました。筆者は普段以下の様に、server.use
内にjest.fn
を仕込みテストしています。例えば API が2回呼ばれたことを検証する場合、次の様になります。
const server = setupMockServer(...handlers);
describe("API call の検証", () => {
const mockFn = jest.fn();
beforeEach(() => {
server.use(
rest.get("/path/to/api", async (req, res, ctx) => {
mockFn(); // <- here
return res(ctx.json({}));
})
);
});
test("ボタンを押下すると、API が呼ばれる", async () => {
render(<MyComponent />);
const button = await screen.findByRole("button");
userEvent.click(button);
userEvent.click(button);
await waitFor(() => expect(mockFn).toHaveBeenCalledTimes(2));
});
});
このアプローチを利用すれば、API がどの様に呼び出されたのか、内訳を詳細に調べることができます。MSW のハンドラー内で検証したい対象を抽出し、mockFn
の引数として渡します。これで axios の mock と同等の検証が出来ます。
const server = setupMockServer(...handlers);
describe("API call の検証", () => {
const mockFn = jest.fn();
beforeEach(() => {
server.use(
rest.get("/path/to/api", async (req, res, ctx) => {
const target = req.url.searchParams.get("search")
mockFn(target); // <- here
return res(ctx.json({}));
})
);
});
test("「test」と入力しエンターキーを押すと、?search=test 付きで API が呼ばれる", async () => {
render(<MyComponent />);
const searchbox = await screen.findByRole("searchbox");
const text = "test";
userEvent.type(searchbox, `${text}{enter}`);
await waitFor(() => expect(mockFn).toBeCalledWith(text));
});
});
この方法は axios の mock よりも手間かもしれませんが、リクエスト詳細を検証するためには、都合が良いこともあります。例えば「リクエスト header に任意の値が挿入されていること」というケースです。req.headers.get
で、検査対象の値を抽出します。
server.use(
rest.get("/path/to/api", async (req, res, ctx) => {
const target = req.headers.get("x-msw-request-id")
mockFn(target);
return res(ctx.json({}));
})
);
axios の mock を使っても同等の検証は可能ですが、Native Fetch API でも同じテストコードで検証できるため「ライブラリ毎にテストコードが違う」という事になりません。Jest は E2E テストフレームより低コストですから、CI の実行時間(待ち時間)を短縮することができます。MSW と併用し、テストケースの範囲を広げてみてはいかがでしょうか。
Discussion