メインコンテンツへスキップ
バージョン: 30.0

手動モック

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

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

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

手動モックは、モジュールの直下にある__mocks__/サブディレクトリにモジュールを記述して定義します。たとえば、modelsディレクトリにあるuserモジュールをモックするには、user.jsというファイルを作成し、models/__mocks__ディレクトリに配置します。

注意

__mocks__フォルダは大文字と小文字を区別するため、ディレクトリ名を__MOCKS__とすると一部のシステムで問題が発生します。

メモ

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

Nodeモジュールのモック

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

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

注意

Nodeの組み込みモジュール(例:fspath)をモックしたい場合、例えば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が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モジュールの手動モックを作成します。この手動モックは、テスト用にカスタマイズしたfs APIのバージョンを実装します:

__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を使用し、そのデフォルト動作をオーバーライドしています。これは推奨されるアプローチですが、完全に任意です。自動モックを全く使用したくない場合は、モックファイルから独自の関数をエクスポートできます。完全な手動モックの欠点は、それが「手動」であること、つまりモック対象のモジュールが変更されるたびに手動で更新する必要があることです。このため、ニーズに合う場合は自動モックを使用または拡張することが最善です。

マニュアルモックと実際の実装を同期させ続けるためには、マニュアルモック内でjest.requireActual(moduleName)を使用して実際のモジュールを読み込み、モック関数で修正してからエクスポートすると便利な場合があります。

この例のコードはexamples/manual-mocksで確認できます。

ESモジュールインポートでの使用

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

注意

ECMAScriptモジュールサポートを有効にしている場合、jest.mock呼び出しはモジュールの最上位に巻き上げられません。ESMモジュールローダーは常にコード実行前に静的インポートを評価します。詳細はECMAScriptModulesを参照してください。

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

Jestが使用するDOM実装であるJSDOMにまだ実装されていないメソッドをコードが使用している場合、テストは容易ではありません。例えば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...
});