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.