ES6 クラスのモック
Jest は、テストしたいファイルにインポートした ES6 クラスをモックすることもできます。
ES6 クラスというのは、いくつかの糖衣構文を加えたコンストラクタ関数です。 したがって、ES6 クラスのモックは、何らかの関数であるか、もう一つの ES6 クラス (繰り返しますが、これは別の関数です) になります。 そのため、モック関数を使用することでモックを作成できます。
ES6 クラスの例
具体例として、音楽ファイルを再生するクラス SoundPlayer
とそのクラスを使用する消費者クラス SoundPlayerConsumer
について考えてみましょう。 SoundPlayerConsumer
のテスト内で、SoundPlayer
クラスをモックするには、次のようなコードを書きます。
export default class SoundPlayer {
constructor() {
this.foo = 'bar';
}
playSoundFile(fileName) {
console.log('Playing sound file ' + fileName);
}
}
import SoundPlayer from './sound-player';
export default class SoundPlayerConsumer {
constructor() {
this.soundPlayer = new SoundPlayer();
}
playSomethingCool() {
const coolSoundFileName = 'song.mp3';
this.soundPlayer.playSoundFile(coolSoundFileName);
}
}
ES6 クラスのモックを作る4つの 方法
自動モック
jest.mock('./sound-player')
を呼ぶと、便利な "自動モック" を返してくれます。 これは、クラスのコンストラクタおよびすべてのメソッドの呼び出しをスパイするのに使用できます。 この関数は ES6 クラスをモックコンストラクタに置き換え、すべてのメソッドを、常に undefined
を返すモック関数に置き換えます。 メソッドの呼び出しは theAutomaticMock.mock.instances[index].methodName.mock.calls
に保存されます。
If you use arrow functions in your classes, they will not be part of the mock. なぜなら、アロー関数はオブジェクトの プロトタイプには現れず、単に関数への参照を保持しているプロパティに過ぎないためです。
クラスの実装を置き換える必要がない場合は、このメソッドを使用するのが最も簡単な選択肢です。 例:
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
jest.mock('./sound-player'); // SoundPlayer is now a mock constructor
beforeEach(() => {
// Clear all instances and calls to constructor and all methods:
SoundPlayer.mockClear();
});
it('We can check if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it('We can check if the consumer called a method on the class instance', () => {
// Show that mockClear() is working:
expect(SoundPlayer).not.toHaveBeenCalled();
const soundPlayerConsumer = new SoundPlayerConsumer();
// Constructor should have been called again:
expect(SoundPlayer).toHaveBeenCalledTimes(1);
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
// mock.instances is available with automatic mocks:
const mockSoundPlayerInstance = SoundPlayer.mock.instances[0];
const mockPlaySoundFile = mockSoundPlayerInstance.playSoundFile;
expect(mockPlaySoundFile.mock.calls[0][0]).toBe(coolSoundFileName);
// Equivalent to above check:
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
expect(mockPlaySoundFile).toHaveBeenCalledTimes(1);
});
マニュアルモック
マニュアルモックを作るには、モックの実装を __mocks__
ディレクトリに保存します。 これにより、特定の実装をテストファイル全体で使用することができるようになります。
// Import this named export into your test file:
export const mockPlaySoundFile = jest.fn();
const mock = jest.fn().mockImplementation(() => {
return {playSoundFile: mockPlaySoundFile};
});
export default mock;
すべてのインスタンスで共有される、モックとモックメソッドをインポートします。
import SoundPlayer, {mockPlaySoundFile} from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
jest.mock('./sound-player'); // SoundPlayer is now a mock constructor
beforeEach(() => {
// Clear all instances and calls to constructor and all methods:
SoundPlayer.mockClear();
mockPlaySoundFile.mockClear();
});
it('We can check if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it('We can check if the consumer called a method on the class instance', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName);
});