Manual Mocks
マニュアルモックはモックデータを返す機能をスタブするために使用します。 例えば、ウェブサイトやデータベースのような外部リソースにアクセスする代わりに、偽のデータが使えるマニュアルモックが欲しいと考えるでしょう。 これによりテストは高速で信頼性の高いものになります。
マニュアルモックは モジュールのディレクトリ直下の__mocks__/
サブディレクトリにモックモジュールを作成することで定義します。 例えばmodels
ディレクトリに user
と呼ばれるモジュールをモックを作成するには、 models/__mocks__
ディレクトリにuser.js
というファイルを作成して配置します。 モックしたいモジュールがnodeモジュール(例えばfs
など) の場合、そのモックはnode_modules
ディレクトリ直下の__mocks__
ディレクトリに格納して下さい。 例:
.
├── config
├── __mocks__
│ └── fs.js
├── models
│ ├── __mocks__
│ │ └── user.js
│ └── user.js
├── node_modules
└── views
与えられたモジュールにマニュアルモックが存在する場合、Jestのモジュールシステムはjest.mock('moduleName')
によってモックモジュールが明示的に呼び出された時に使用します。 しかしマニュアルモックは jest.mock('moduleName')
が呼ばれていない場合でも、nodeモジュールより優先されます。 この振る舞いを止めさせるには、本物のモジュールを使用するべきテストの中で jest.unmock('moduleName')
を明示的に呼び出す必要があります。
以下は指定されたディレクトリ内の全てのファイルの要約を提供するモジュールの分かりやすい例です。
// 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.genMockFromModule('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;
そしてテストを書きましょう:
// __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.genMockFromModule
関数を使用し、そのデフォルトの振る舞いをオーバーライドしています。 これはお勧めの方法ですが、必ずしもこのようにする必要はありません。 自動モックを全く使いたくない場合は、単純にモックファイルから独自に作成した関数をエクスポートすれば良いだけです。 完全に手作業でモックを作成することの欠点の1つは、手作業であることです - つまりモックする対象のモジュールが変更するごとに手動で更新しなければならないことです。 このため、目的に沿うのなら自動モックをそのまま使うか拡張することをお勧めするわけです。
マニュアルモックとモジュールの実物が同期するようにしておくには、マニュアルモックの中でrequire.requireActual(moduleName)
関数で本物を呼び出してエクスポートする前にそれらをモック関数に置き換えるようにすると便利です。
このコードの例はexamples/manual_mocksで確認できます。
ES module importを利用する #
ES module importsを使用している場合、通常はテストファイルの先頭でimport
宣言を書くことが多くあるでしょう。 しかしモジュールの前にモックを使用するようJestに指示したいことがしばしばあります。 このため、Jestは自動的にjest.mock
コールを自動的にモジュールの先頭に(importを行う前に)移動します。 このことについての詳細および実例については、 このリポジトリを参照して下さい。