😎

Node.jsのfetchをmockしたい

2024/01/11に公開

概要

Node.jsのfetchをmockする場合、 fetch-mockを利用するのが一般的なようです。

https://www.npmjs.com/package/fetch-mock

簡単な処理をmockしたかっただけなので、わざわざモジュールを使うまででもないと思い、自分でmockする処理を作成しました。

試したNode.jsのバージョンは v18.17.1です。

fetchについて

Node.jsではv18.0.0からfetchが利用できるようになっています。
https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#fetch

A browser-compatible implementation of the fetch() function. と書いてある通り、基本的にブラウザのfetchと同じ動きをするようです。
globalで定義されているので、これを置き換えられれば大丈夫そうです。

ブラウザ側のfetchについて

https://developer.mozilla.org/en-US/docs/Web/API/fetch

fetchはPromise<Response>を返すので、それを実装しておけばよさそうです。

実装

今回はjsonの結果のみ欲しかったので、他は極力空の値を返すようにしています。

global.fetch = () => new Promise((resolve) => resolve(({
  ok: true,
  headers: {
    append: () => { },
    delete: () => { },
    get: () => null,
    has: () => false,
    set: () => { },
    forEach: () => { },
    // node18だと以下のメソッドがあるとエラーになる。
    // mdnのドキュメントにはある
    // getSetCookie: () => [],
    // entries: function (): IterableIterator<[string, string]> {
    //   throw new Error('Function not implemented.');
    // },
    // keys: function (): IterableIterator<string> {
    //   throw new Error('Function not implemented.');
    // },
    // values: function (): IterableIterator<string> {
    //   throw new Error('Function not implemented.');
    // },
    // [Symbol.iterator]: function (): IterableIterator<[string, string]> {
    //   throw new Error('Function not implemented.');
    // }
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  } as any,
  redirected: false,
  status: 200,
  statusText: 'OK',
  type: 'basic',
  url: '',
  // mockしたいのはこの部分
  json: () => new Promise((r) => r({
    hoge: "hoge",
    foo: "bar",
  })),
  body: null,
  bodyUsed: false,
  arrayBuffer: () => new Promise((r) => r(new ArrayBuffer(0))),
  blob: () => new Promise((r) => r(new Blob())),
  formData: () => new Promise((r) => r(new FormData())),
  text: () => new Promise((r) => r('')),
  clone: function () { return this },
})));

headersは本来であれば、Headersのinterfaceを満たすものになります。
しかしながら、as anyを設定しないと実行時に以下のようなエラーが発生します。

Object literal may only specify known properties, and 'getSetCookie' does not exist in type 'Headers'.

結論

fetchの関数をライブラリを利用せずに手軽にmockが書けました。
原因はわかっていませんが、ブラウザ側にしたがってそのまま記述すると型のエラーになるので、 as any で回避する必要がありました。

Discussion