やりたいこと
ライブラリから外部の Web APIに通信している機能がありました。
どうやってテストしようか...🤔
そこで Mock Service Worker の登場です 🎉
msw を使って Jest でテストできるようにしていきます。
mswとは
次世代の API モッキング
ネットワーク レベルでリクエストをインターセプト(横取り)してモックします。
テスト、開発、およびデバッグのために同じモック定義をシームレスに再利用します。
ネットワークレベルでモック化できるので、ライブラリの実装も含めてテストできて good 👍
やること一覧
- リクエストを確認する
- ライブラリをインストールする
- Service Workerを生成する
- モックを作る
- テストコードを書く
リクエストを確認する
まずはモック化する対象を確認します。
デベロッパーツールの Network タブで Request URL, Request Method, Response を確認します。


ライブラリをインストールする
Jestで動かすには whatwg-fetch が必要なので一緒にインストールしておきます。
$ npm --save-dev install msw whatwg-fetch
Service Workerを生成する
msw が使用する Service Worker を生成します。
npx msw init ./public --save
モックを作る
ディレクトリを作る
モック関連のモジュールを管理するディレクトリを作成します。
mkdir src/mocks
レスポンスを定義する
ファイルが肥大化しないように、エンドポイントごとにファイルを作成します。
例えば https://***.com/api/items
のモックを作成する場合、src/moks/api/items.ts
を作成します。
import { ResponseResolver, MockedRequest, restContext } from 'msw';
export const get: ResponseResolver<MockedRequest, typeof restContext> = (
_req,
res,
ctx
) => {
return res(
ctx.status(200),
ctx.json([
{
id: 1,
name: 'ミネラルウォーター',
price: 150,
},
{
id: 2,
name: 'オレンジジュース',
price: 200,
},
]
)
);
};
リクエストハンドラーを作る
rest[METHOD] にリクエストパスを指定して、リクエストハンドラーを作成します。
import { rest } from 'msw';
import * as items from './api/items';
const url = (path: string): string => {
return new URL(path, 'https://***.com').toString();
};
export const handlers = [
rest.get(url('api/items'), items.get),
];
Nodeで動くように設定する
Jest でモックを使うのでサーバー (Node) で動くように設定します。
src/mocks/server.ts
を作成します。
server.ts
では、リクエストハンドラーを使用してサーバーインスタンスを作成します。
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
これでモックの作成は終わりです😌☕️
テストコードを書く
作ったモックを使用してテストコードを書いていきます。
server.listen()
でモックサーバーをスタートします。
server.resetHandlers()
で追加されたすべてのリクエストハンドラーを削除します。
server.close()
でモックサーバーをクローズします。
import { rest } from 'msw';
import { server } from '../../mocks/server';
import 'whatwg-fetch';
describe('fetchItems', () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test(...);
});
異常系のテストも書いていきます。
異なるレスポンスを定義したいときは server.use で新しいリクエストハンドラーを作成してサーバーインスタンスに追加してある必要があります。
res.once
とすることでこのリクエストハンドラーは一度だけ実行されます。
...
test('異常系', async () => {
server.use(
rest.get('https://***.com/api/items', (req, res, ctx) => {
return res.once(
ctx.status(500),
ctx.json({ message: 'Internal Server Error' })
);
})
);
...
});
...
これで無事テストできました🙌
参考