元々「ServiceWorker をベースにした技術をわざわざテストに持ち込む意味とは?」と思って、msw をテスト環境で使う意義について懐疑的だったのですが、いざ使ってみるとすごく便利ですね。ServiceWorker 云々以前に、ネットワークリクエストの mock ライブラリとして、インターフェイスがとても使いやすいです。
特に handler の mock や spy が簡単にできるのが嬉しいです。Jest と組み合わせた時の例だと、以下のようなイメージ。
// jest.setup.ts import { server } from './src/test-utils/msw'; beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close());
// src/test-utils/msw.ts import { setupServer } from 'msw/node'; import { graphql, rest } from 'msw'; export type TodoAddRequest = { title: string }; export type TodoAddResponse = { type: 'success' } | { type: 'error', error: string }; const handlers = [ // デフォルトの handler をここで定義しておく。 rest.post<TodoAddRequest>('/todo/add', (req, res, ctx) => { return res( ctx.json<TodoAddResponse>({ type: 'success' }), ); }), // ... ]; export const server = setupServer(...handlers);
// src/components/TodoList.test.ts import { server, TodoAddResponse } from '../test-utils/msw'; // mock の例 test('todo 追加時にエラーになったら、エラーメッセージが表示される', async () => { server.use(rest.post('/todo/add', (req, res, ctx) => { // 既に `/todo/add` 向けに登録されている handler を無視して、 // この handler で上書きする (mock) return res( ctx.status(500), ctx.json<TodoAddResponse>({ type: 'error', error: 'TODO は 10 個までしか登録できません。', }), ); })); render(<TodoList />); await userEvent.type(screen.getByLabelText('title'), '買い物に行く'); await userEvent.click(screen.getByRole('button', { name: '追加' })); expect(screen.getByRole('alert')).toHaveTextContent( 'TODO は 10 個までしか登録できません。', ); }); // spy の例 test('todo を追加できる', async () => { const todoAddSpy = jest.fn(); // これを `server.use` に渡す。 `return res(...)` していないので、 // デフォルトの handler まで貫通する server.use(rest.post('/todo/add', todoAddSpy)); render(<TodoList />); await userEvent.type(screen.getByLabelText('title'), '買い物に行く'); expect(todoAddSpy).not.toBeCalled(); await userEvent.click(screen.getByRole('button', { name: '追加' })); // handler が期待通りの呼ばれ方をしたかをチェック expect(todoAddSpy).toBeCalled(); expect(todoAddSpy).toBeCalledWith(expect.objectContaining( body: { title: '買い物に行く', }, )); }
テストケース側で server.use
で return res(...)
を含む handler を登録すれば mock が、return res(...)
を含まない handler を登録すれば spy ができます*1。便利ですね。
*1:よく考えてみたら、デフォルトの handler の入力を傍受できるだけで出力は傍受できないので、spy と言えるかどうか微妙な気がしてきた...