Node.js
jest
fetch

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

opengraph.png

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

Jestの初期化

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

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

$ yarn init

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

$ yarn add --dev jest babel-core babel-jest babel-plugin-transform-es2015-modules-commonjs node-fetch
.babelrc
{
  "env": {
    "test": {
      "plugins": [
        "transform-es2015-modules-commonjs"
      ]
    }
  }
}

テストファイルを置く

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

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

テスト対象コード

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;

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-fetchjestのモックモジュールに置き換える宣言をしています。
fetch.mockImplementation(() => dummyResponse)で実際にどんなレスポンスを返すかの実装を行っています。

うまく動きました!

ハマったポイント

proxyquire

JavaScriptのモック用ライブラリにproxyquireがあります。
テストするときには非常に協力なモックライブラリで、具体的にはrequireを良い感じに乗っ取ってモックされた関数などを返します。
proxyquireでDI もっとモックテストしよう

ただJestとの相性がよくないらしく、Jestではproxyquireが動かない、という報告がありました。
Does not work with proxyquire
実際に今回は動きませんでした。このIssueではjest.mockを使え、となっていますね。

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

nock

fetchのモックするためのライブラリにnockがあります。こちらも良く出来たライブラリなのですが、うまく動きませんでした。
【Node.js】nockでtest!

jest-fetch-mock

Jest用のfetchをモックする専用のライブラリ 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?


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