Node.jsのテストでJestを使ってfetchをモックするのは意外に大変

![opengraph.png](https://qiita-image-store.s3.amazonaws.com/0/47327/2f66dfc1-922d-f998-c787-5b9c95cee827.png)


Jestを使ってfetch()をモックしたかったです。
いくつかのハマりポイントでハマってしまったのですが解決できました。

# Jestの初期化

Jest本体とBabel、今回テストを行いたいnode-fechのモジュールを入れます。

リポジトリを初期化しておきます。

```bash
$ yarn init
```

BabelはJestのテストファイルでimport文などのES6記法を使うために入れています。

```bash
$ yarn add --dev jest babel-core babel-jest babel-plugin-transform-es2015-modules-commonjs node-fetch
```

```conf:.babelrc
{
  "env": {
    "test": {
      "plugins": [
        "transform-es2015-modules-commonjs"
      ]
    }
  }
}
```

# テストファイルを置く

テストコードを先に書いてしまいます。

```javascript:index.test.js
import fetch from 'node-fetch'

jest.mock('node-fetch', () => jest.fn())

describe('fetch-mock test', () => {
    it('check fetch mock test', async () => {
        const dummyResponse = Promise.resolve({
            ok: true,
            status: 200,
            json: () => {
                return {};
            },
        });

        fetch.mockImplementation(() => dummyResponse)
        await dummyResponse;

        console.log(dummyResponse);
    });
});
```

動かしてみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/47327/acd9b68f-ea70-999c-2bda-8c785dc02285.png)

# テスト対象コード

```javascript:index.js
'use strict'
var fetch = require('node-fetch');

const makeRequest = async () => {
	const res = await fetch("http://httpbin.org/get");
	const resJson = await res.json();
	return resJson;
};

module.exports = makeRequest;

```

```javascript:index.test.js
import fetch from 'node-fetch'

jest.mock('node-fetch', () => jest.fn())

describe('fetch-mock test', () => {
    it('check fetch mock test', async () => {
        const dummyResponse = Promise.resolve({
            ok: true,
            status: 200,
            json: () => {
                return {};
            },
        });
        fetch.mockImplementation(() => dummyResponse)
        await dummyResponse;

        var makeRequest = require('./index'); // テストしたいモジュールを読み込む

        makeRequest().then(function (data) { // success
            console.log('got data', data); // ダミーレスポンスが帰ってきている
        }).catch((e) => { // error
            console.log(e.message)
        });
    });
});

```

`jest.mock('node-fetch', () => jest.fn())`をすることで、本体コード側で`node-fetch`を`jest`のモックモジュールに置き換える宣言をしています。
`fetch.mockImplementation(() => dummyResponse)`で実際にどんなレスポンスを返すかの実装を行っています。


うまく動きました！


# ハマったポイント
## proxyquire
JavaScriptのモック用ライブラリに[proxyquire](https://github.com/thlorenz/proxyquire)があります。
テストするときには非常に協力なモックライブラリで、具体的には`require`を良い感じに乗っ取ってモックされた関数などを返します。
[proxyquireでDI もっとモックテストしよう
](https://qiita.com/okunokentaro/items/46781adda80e9d53f7ee)

ただJestとの相性がよくないらしく、Jestではproxyquireが動かない、という報告がありました。
[Does not work with proxyquire](https://github.com/facebook/jest/issues/1937)
実際に今回は動きませんでした。このIssueでは`jest.mock`を使え、となっていますね。

proxyquire自体も古い[^1]ようで、Jestでは`jest.mock`を使っていくほうが良いようです。

[^1]: 2018年現在では2015年ぐらいの記事が多かったです。

## nock
fetchのモックするためのライブラリに`nock`があります。こちらも良く出来たライブラリなのですが、うまく動きませんでした。
[【Node.js】nockでtest！
](https://qiita.com/chihiro/items/6763a13a384823d4e51a)

## jest-fetch-mock
Jest用のfetchをモックする専用のライブラリ [jest-fetch-mock](https://www.npmjs.com/package/jest-fetch-mock) があります。
`jest.mock()`汎用のモックシステムなのですが、fetchレスポンスにフォーカスしたライブラリのようです。
こちらもうまく動かせませんでした。


# 謝辞
今回の件でうまく動かせず色々ヘルプをもらいました。
@say3no @ketch123 @ur0n @naname 
ありがとうございました！

StackOverFlowで回答してくれたAndreas Köberleありがとう！
[How can I mock fetch function in Node.js by Jest?](https://stackoverflow.com/questions/53484219/)