本文へ移動
バージョン: 29.7

手動モック

手動モックは、モックデータを使用して機能をスタブアウトするために使用されます。たとえば、ウェブサイトやデータベースなどのリモートリソースにアクセスする代わりに、偽のデータを使用できる手動モックを作成できます。これにより、テストが高速になり、不安定になることがなくなります。

ユーザーモジュールのモック化

手動モックは、モジュールに隣接する__mocks__/サブディレクトリにモジュールを作成することで定義されます。たとえば、modelsディレクトリにあるuserというモジュールをモックするには、user.jsというファイルを作成し、models/__mocks__ディレクトリに配置します。

警告

__mocks__フォルダは大文字と小文字を区別するため、__MOCKS__という名前のディレクトリは、一部のシステムでは動作しません。

注意

テストでそのモジュールをrequireする場合(つまり、実際の実装ではなく手動モックを使用したい場合)、jest.mock('./moduleName')を明示的に呼び出すことが**必須**です。

Nodeモジュールのモック化

モックしているモジュールがNodeモジュール(例:lodash)の場合、モックはnode_modulesに隣接する__mocks__ディレクトリに配置する必要があります(rootsをプロジェクトルート以外のフォルダを指すように設定していない限り)、そして**自動的に**モックされます。jest.mock('module_name')を明示的に呼び出す必要はありません。

スコープ付きモジュール(スコープ付きパッケージとも呼ばれる)は、スコープ付きモジュールの名前と一致するディレクトリ構造にファイルを作成することでモックできます。たとえば、@scope/project-nameというスコープ付きモジュールをモックするには、__mocks__/@scope/project-name.jsにファイルを作成し、それに応じて@scope/ディレクトリを作成します。

警告

Nodeの組み込みモジュール(例:fsまたはpath)をモックしたい場合は、組み込みモジュールはデフォルトではモックされないため、例としてjest.mock('path')を明示的に呼び出すことが**必須**です。

.
├── config
├── __mocks__
│   └── fs.js
├── models
│   ├── __mocks__
│   │   └── user.js
│   └── user.js
├── node_modules
└── views

特定のモジュールに手動モックが存在する場合、Jestのモジュールシステムは、jest.mock('moduleName')を明示的に呼び出したときにそのモジュールを使用します。ただし、automocktrueに設定されている場合、jest.mock('moduleName')が呼び出されていなくても、自動的に作成されたモックではなく、手動モックの実装が使用されます。この動作を無効にするには、実際のモジュール実装を使用するテストでjest.unmock('moduleName')を明示的に呼び出す必要があります。

情報

適切にモックするには、jest.mock('moduleName')require/import文と同じスコープにある必要があります。

これは、特定のディレクトリ内のすべてのファイルのサマリーを提供するモジュールを持つ、無理やりな例です。この場合、コア(組み込み)のfsモジュールを使用します。

FileSummarizer.js
'use strict';

const fs = require('fs');

function summarizeFilesInDirectorySync(directory) {
return fs.readdirSync(directory).map(fileName => ({
directory,
fileName,
}));
}

exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;

テストで実際にディスクにアクセスするのを避けたいので(これは非常に遅く、壊れやすい)、自動モックを拡張することによって、fsモジュールのマニュアルモックを作成します。私たちのマニュアルモックは、テストのために構築できるfsAPIのカスタムバージョンを実装します。

__mocks__/fs.js
'use strict';

const path = require('path');

const fs = jest.createMockFromModule('fs');

// This is a custom function that our tests can use during setup to specify
// what the files on the "mock" filesystem should look like when any of the
// `fs` APIs are used.
let mockFiles = Object.create(null);
function __setMockFiles(newMockFiles) {
mockFiles = Object.create(null);
for (const file in newMockFiles) {
const dir = path.dirname(file);

if (!mockFiles[dir]) {
mockFiles[dir] = [];
}
mockFiles[dir].push(path.basename(file));
}
}

// A custom version of `readdirSync` that reads from the special mocked out
// file list set via __setMockFiles
function readdirSync(directoryPath) {
return mockFiles[directoryPath] || [];
}

fs.__setMockFiles = __setMockFiles;
fs.readdirSync = readdirSync;

module.exports = fs;

これでテストを書きます。この場合、fsはNodeの組み込みモジュールであるため、jest.mock('fs')を明示的に呼び出す必要があります。

__tests__/FileSummarizer-test.js
'use strict';

jest.mock('fs');

describe('listFilesInDirectorySync', () => {
const MOCK_FILE_INFO = {
'/path/to/file1.js': 'console.log("file1 contents");',
'/path/to/file2.txt': 'file2 contents',
};

beforeEach(() => {
// Set up some mocked out file info before each test
require('fs').__setMockFiles(MOCK_FILE_INFO);
});

test('includes all files in the directory in the summary', () => {
const FileSummarizer = require('../FileSummarizer');
const fileSummary =
FileSummarizer.summarizeFilesInDirectorySync('/path/to');

expect(fileSummary.length).toBe(2);
});
});

ここに示されている例モックは、jest.createMockFromModuleを使用して自動モックを生成し、そのデフォルトの動作をオーバーライドします。これは推奨されるアプローチですが、完全にオプションです。自動モックをまったく使用したくない場合は、モックファイルから独自の関数をエクスポートできます。完全に手動モックの1つの欠点は、手動であることです。つまり、モックしているモジュールが変更されるたびに手動で更新する必要があります。このため、ニーズに合致する場合は、自動モックを使用または拡張するのが最善です。

手動モックとその実際の実装が同期状態を維持するために、手動モックでjest.requireActual(moduleName)を使用して実際のモジュールをrequireし、エクスポートする前にモック関数で修正すると便利です。

この例に関するコードは、examples/manual-mocksにあります。

ESモジュールインポートとの連携

ESモジュールインポートを使用している場合、通常はテストファイルの先頭にimport文を配置する傾向があります。しかし、多くの場合、モジュールが使用する前にモックを使用するようにJestに指示する必要があります。このため、Jestはjest.mock呼び出しをモジュールの先頭(インポート前)に自動的にホイスティングします。これの詳細と動作を確認するには、このリポジトリを参照してください。

警告

ECMAScriptモジュールサポートを有効にした場合、jest.mock呼び出しはモジュールの先頭にホイスティングできません。ESMモジュールローダーは、コードを実行する前に常に静的インポートを評価します。ECMAScriptModulesを参照してください。

JSDOMで実装されていないメソッドのモック化

一部のコードが、JSDOM(Jestで使用されるDOM実装)がまだ実装していないメソッドを使用している場合、そのテストは容易ではありません。これは、たとえばwindow.matchMedia()の場合です。JestはTypeError: window.matchMedia is not a functionを返し、テストを正しく実行しません。

この場合、テストファイルでmatchMediaをモックすれば問題が解決するはずです。

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});

これは、window.matchMedia()がテストで呼び出される関数(またはメソッド)で使用されている場合に機能します。window.matchMedia()がテスト対象のファイルで直接実行される場合、Jestは同じエラーを報告します。この場合の解決策は、手動モックを別のファイルに移動し、テスト対象のファイルの前にテストにそのファイルを含めることです。

import './matchMedia.mock'; // Must be imported before the tested file
import {myMethod} from './file-to-test';

describe('myMethod()', () => {
// Test the method here...
});