背景
- Node.js v18 で global fetch API が実験的に導入された
- これにより jest 等 Node.js のランタイムで fetch API を利用する際に node-fetch 等の外部ライブラリを導入する必要がなくなった
- しかし通常 React のテスト等で用いる jsdom 環境で利用するにはいくつか苦労があったのでまとめておく
jest のバージョンを確認
Node.js のバージョンが v18 以降であっても jest のバージョンが v28 未満だと fetch API を利用できない。
https://jestjs.io/blog/2022/04/25/jest-28#all-nodejs-globals
package.json で jest のバージョンを ^28
以上に指定して npm install
または yarn install
を実行することで、期待するバージョンの jest をインストールすることができる。
jsdom 環境を拡張
jsdom とは
前提として、ブラウザ上で動作する JavaScript は、生の JavaScript に加えてブラウザ操作用の API を利用可能な環境になっている。
この独自の API はブラウザに実装された機能なので、純粋な JavaScript には含まれていない。従ってブラウザ上では利用できるけど Node.js 上では利用できないオブジェクト等が多く存在する。
しかし、そのままではブラウザ向けに実装した各種コードを Jest 等の Node.js ランタイムでテストしたい時などに不便である。
そこで Node.js のランタイム上でブラウザ独自の API を利用できるように、同じ動きをシミュレートできるような実装を提供してくれるのが jsdom である。
jest からは jest.config.js
内で下記のように設定することで jsdom の実装を global に反映している。
testEnvironment: "jest-environment-jsdom"
ところが、この jest-environment-jsdom が global オブジェクトを差し替えているために、 Node.js v18 で導入された global fetch が差し替え後の global に反映されておらず、実際に fetch を利用しようとすると ReferenceError: fetch is not defined
というエラーが発生することになる。
jsdom 環境を拡張する
jest のドキュメントによれば、 testEnvironment は自作できるらしい。
You can create your own module that will be used for setting up the test environment. The module must export a class with
setup
,teardown
andgetVmContext
methods. You can also pass variables from this module to your test suites by assigning them tothis.global
object – this will make them available in your test suites as global variables. The constructor is passedglobalConfig
andprojectConfig
as its first argument, andtestEnvironmentContext
as its second.
これを利用して、このコメントで提案されている方法を試してみる。
import JSDOMEnvironment from "jest-environment-jsdom";
// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
super(...args);
// FIXME https://github.com/jsdom/jsdom/issues/1724
if (!this.global.fetch) {
this.global.fetch = fetch;
this.global.Headers = Headers;
this.global.Request = Request;
this.global.Response = Response;
}
}
}
{
"testEnvironment": "./FixJSDOMEnvironment.ts"
}
この状態で jest を実行することで、無事に node-fetch の polyfill なしで Node.js 標準の fetch API を利用することができた。
MSW の Node.js v18 対応
無事に Node.js 標準の fetch API を利用することができたものの、別の問題として API のモックに利用している MSW が Node.js v18 に対応しておらず、 API アクセスをモックできないことが分かった。
公式の issue を見ると、2023年1月時点では対応の意思こそあるもののメンテナが忙しくて作業できていないという悲痛なコメントが見られる。
https://github.com/mswjs/msw/issues/1388#issuecomment-1401706972
結論
以上より、まだ Experimental な Node.js 純正の fetch API にこだわるよりも、 jsdom
や MSW の対応が完了するまでは今まで通り node-fetch
による polyfill に頼った方が良さそうに思った。
(MSW を使っていないプロジェクトなら問題ないかもしれないが、そこは各々で判断してほしい。)
// 今まで通りの polyfill
import "isomorphic-fetch"