Bypassing module mocks
Jest allows you to mock out whole modules in your tests, which can be useful for testing if your code is calling functions from that module correctly. ただし、 テストファイルでモックされたモジュールの一部を使用したい場合があります。その場合、モック化されたモジュールではなく元の実装にアクセスします。
次の createUser 関数のテストケースを書く場合を考えます。
// createUser.js
import fetch from 'node-fetch';
export const createUser = async () => {
const response = await fetch('http://website.com/users', {method: 'POST'});
const userId = await response.text();
return userId;
};
テストでは、実際にネットワークリクエストを行わなくても呼び出されるように、 fetch 関数をモックする必要があります。 ただし、 作成したユーザーのIDを取得するのに使われているので、fetch の戻り値を ( Promiseにラップされた)Response でモックする事も必要です。 ですから、最初は次のようなテストを書くことでしょう。
jest.mock('node-fetch');
import fetch, {Response} from 'node-fetch';
import {createUser} from './createUser';
test('createUser calls fetch with the right args and returns the user id', async () => {
fetch.mockReturnValue(Promise.resolve(new Response('4')));
const userId = await createUser();
expect(fetch).toHaveBeenCalledTimes(1);
expect(fetch).toHaveBeenCalledWith('http://website.com/users', {
method: 'POST',
});
expect(userId).toBe('4');
});
しかし、上記のテストを実行した場合、createUser関数は次のようなエラーを出力して失敗するでしょう: TypeError: response.text is not a function これは、 jest.mock をテストファイルの最初に呼び出したことで、node-fetchから import した Response クラスがモックされ、本来あるべき振る舞いをしなくなったためです。
To get around problems like this, Jest provides the jest.requireActual helper. To make the above test work, make the following change to the imports in the test file:
// BEFORE
jest.mock('node-fetch');
import fetch, {Response} from 'node-fetch';
// AFTER
jest.mock('node-fetch');
import fetch from 'node-fetch';
const {Response} = jest.requireActual('node-fetch');
This allows your test file to import the actual Response object from node-fetch, rather than a mocked version. This means the test will now pass correctly.