この記事は、LITALICO Engineers Advent Calender 2023 シリーズ2の9日目の記事です。ただし、12月23日に投稿しています。
はじめに
LITALICO プロダクトエンジニアリング(PE)部の片桐英人(かたぎり えいと)です。
最近、仕事ナビのフロントエンドのテストや Storybook で使用している Mock Service Worker(msw)を 1.x から 2.0 に更新しました。仕事ナビのメンバーにそのことを伝えるために書いています。
この記事を記載している時点で、最新は、2.0.11 になっていますね…
Mock Service Worker (msw) とは?
Mock Service Worker(msw)1 は、Service Worker 2 を使って、HTTP リクエストとそのレスポンスをモックするための JavaScript のライブラリ(npm パッケージ)です。
MSW 2.0
MSW 2.0 は、2023年10月末にリリースされました 3。このリリースで、モックの定義方法など多くの破壊的な変更がありました。今回の更新で変更した変更点は、以下になります。記載していない変更方法については、1.x → 2.x を参照してください。
変更内容
import 内容の変更
MSW 1.x では、REST API のモックの定義に rest
を使用していましたが、 MSW 2.0 では、http
に変更されました。また、レスポンスを作成するための HttpResponse
というクラスが必要になります。
変更前
import { rest } from 'msw';
変更後
import { http, HttpResponse } from 'msw';
handler の定義の変更
MSW 1.x から 2.0 では、REST API のモックする handler の定義方法が大きく変わりました。変更されて、簡単に定義できるようになっています。
変更前
(req, res, ctx) => {
return res(ctx.json({ userId: 1 });
}
変更後
() => {
return HttpResponse.json({ userId: 1 });
}
handler 内でクエリパラメータの取得
MSW 2.0 では、クエリパラメータを取得する場合は、handler のパラメータの info
オブジェクトから params
要素を展開して取得するようになっています。
変更前
(req, res, ctx) => {
const { user_id } = req.params;
...
}
変更後
({ params }) => {
const { user_id } = params;
...
}
handler 内でリクエストボディの取得
MSW 2.0 では、リクエストボディを取得する場合は、handler のパラメータの info
オブジェクトから request
要素を展開して取得するようになっています。
変更前
async (req, res, ctx) => {
const body = await req.json();
...
}
変更後
async ({ request }) => {
const body = await request.json();
...
}
レスポンスの HTTP ステータスの変更
レスポンスの HTTP ステータスを 200
以外にしたい場合は、HttpResponse
の第2引数のオブジェクト内の status
で指定します。
変更前
async (req, res, ctx) => {
return res(ctx.status(500));
}
変更後
async () => {
return new HttpResponse('Internal Server Error', { status: 500 });
}
更新時につまずいた点
更新中に色々なエラーが発生しました。いくつかは、「1.x から 2.x への移行ガイド」の "Frequent issues" に記載されているようにすれば、解決できました。
自分で試行錯誤して解決した点を説明します。
undici
移行ガイドで、ポリフィルを追加する際、"Make sure to install undici
." なので、undici を追加する必要があります。今回の更新作業で msw 2.0.9 にする場合は、undici のバージョンが 5.28.2 にする必要がありました。undici の最新のバージョンは、6.2.1 なので、インストール時に注意が必要です。
$ yarn add -D undici@5.28.2
この時、undici
というパッケージを追加する必要がありました。バージョンを 5.28.2 に固定する必要がありました。
$ yarn add -D undici@5.28.2
モックへのリクエストが止まる
jest を使ったテスト内で、MSW を使って API リクエストをモックしている箇所で、MSW 2.0 へ更新した後、止まってしまうようになってしまいました。
日時を固定するために以下のようにしている部分がありました。
jest.useFakeTimers({
now: new Date('2023-12-09'),
});
ここの箇所をコメントアウトして、テストを実行すると失敗しますが成功するので、この辺りの部分が問題だろうということがわかりました。最終的には、
jest.useFakeTimers({
doNotFake: ['queueMicrotask'],
now: new Date('2023-12-09'),
});
とすることでテストの実行が止まることがなくなりました。理由が良くわかっていないので、機会がある時に調査したいと思います。
TypeError: Invalid base URL
MSW 2.0 に更新後、実行すると "TypeError: Invalid base URL:" というエラーが発生するテストがいくつかありました。バックトレースが以下のようになっていました。
TypeError: Invalid base URL:
at new URLImpl (/.../node_modules/whatwg-url/lib/URL-impl.js:15:15)
at Object.exports.setup (/.../node_modules/whatwg-url/lib/URL.js:54:12)
at new URL (/.../node_modules/whatwg-url/lib/URL.js:115:22)
at toAbsoluteUrl (/.../node_modules/@mswjs/interceptors/lib/node/chunk-5OKLCEIP.js:631:10)
このバックトレースを参考に辿っていくと@mswjs/interceptors/lib/node/chunk-5OKLCEIP.js
の631行めの以下の箇所で location.href
が空文字のためにエラーが発生していることがわかりました。
function toAbsoluteUrl(url) {
if (typeof location === "undefined") {
return new URL(url);
}
return new URL(url.toString(), location.href); // ここ
}
テストのコードを見直してみると以下のように location
オブジェクトをモックしていました。
Object.defineProperty(window, 'location', {
writable: true,
value: {
host: 'example.com',
hostname: 'example.com',
href: '',
port: '',
protocol: 'https:',
},
});
この部分の href
の箇所を https://localhost:3000/
などのように URL として正しい文字列を入れることで、更新前のようにテストが成功するようになりました。
ここでは、明示的に href
を空にしていましたが、location
オブジェクトをモックしている場合は、同様に href
に正しい URL 文字列を入れる必要がありました。
さいごに
MSW 2.0 への更新には、 MSW の API の変更があり、多くの書き換えが必要あり、大変でしたが、新しい API の方が、書きやすくなり、MSW を使ったテストを書く効率が少し上がるのかなぁと思っています。これからも、本番だけでなく、開発やテスト使用するライブラリも積極的に更新していき、良いサービスの迅速に提供できる開発やテスト環境を整備していきたいと思います。