はじめに
jestでmockを使ったテストをすることになった際、色々記事を参考にしていたらなかなかうまくいかなかったため簡易的な備忘録としてうまくいった方法を残しておきます。
詳細説明などは下記記事の方が参考になるかとおもうので、先にこちらを読むことをお勧めします。
- Mock Functions · 公式ドキュメント
- 【備忘録】JestのspyOn()とmock()の使い方について
- [jest]DIしたオブジェクトをmockして、メソッドが呼ばれているかの確認をする
環境
- node: 16.6.2
- @types/jest: 27.0.3
- jest: 27.4.5
- ts-jest: 27.1.1
- ts-node: 9.0.0
- typescript: 4.0.2
前提
ClazzAのexecute関数で、TestRepositoryの各関数が何回呼ばれたかをテストしたい
- ClazzA: 今回テストしたいクラス
- TestRepository: ClazzAのインスタンス化に必要
- 使用するExpect
- 呼び出し回数のチェック:
expect(actual).toBeCalledTimes(n)
expect(actual).not.toBeCalled()
- 引数のチェック(今回の記事では未使用):
expect(actual).toBeCalledWith()
- 呼び出し回数のチェック:
実装方法
インターフェースを実装したMockを作成
Repository Interface の Mock を作成する
-
jest.fn().mockReturnValue(返り値)
任意の値を返すmock関数を作成する。 -
jest.fn<Type, []>().mockImplementation(() => ();
指定した型を返すmockを作成する。 -
mockFn.mockClear();
モックの使用履歴などをクリアする。テストを複数書く時にモック関数の呼び出し回数がどんどん加算されてしまわないようにしておく。 -
手順2で作成したモックをインスタンス化して、テストしたいクラスなどに渡す
import ClazzA from 'path/to/ClazzA';
import TestRepository from 'path/to/TestRepository';
describe("ClazzAのテスト", async () => {
const executeMock = jest.fn().mockReturnValue(Promise.resolve());
const Repository = jest.fn<TestRepository, []>().mockImplementation(() => ({
execute: executeMock
}));
afterEach(() => {
executeMock.mockClear();
});
test("executeMockが呼ばれるテスト", () => {
const a = new ClazzA(new Repository());
await a.execute();
expect(executeMock).toBeCalledTimes(2);
});
});
オブジェクトをモックする方法(spyOnを使用する)
TestRepositoryオブジェクトの Mock を作成する
-
jest.spyOn();
メソッドを使い、追跡したいメソッドを指定する -
(Clazz.prototype.method as jest.Mock).mockClear();
モックの使用履歴をクリアしておく -
(Clazz.prototype.method as jest.Mock).mockImplementation();
で返り値の指定などをしておく -
手順2で作成したモックをインスタンス化して、テストしたいクラスなどに渡す
import ClazzA from 'path/to/ClazzA';
import TestRepository from 'path/to/TestRepository';
describe("ClazzAのテスト", async () => {
beforeEach(() => {
jest.spyOn(TestRepository.prototype, 'sampleMethod');
jest.spyOn(TestRepository.prototype, 'testMethod');
});
afterEach(() => {
(TestRepository.prototype.sampleMethod as jest.Mock).mockClear();
(TestRepository.prototype.testMethod as jest.Mock).mockClear();
});
test("sampleMethod の返り値が0の時 testMethod が呼ばれないテスト", () => {
(TestRepository.prototype.sampleMethod as jest.Mock).mockImplementation(() => {
return 0;
});
const a = new ClazzA(new TestRepository());
await a.execute();
expect(TestRepository.prototype.testMethod).not.toBeCalled();
});
});
おわりに
上記でインターフェースやオブジェクトをモック化することができました。
モック化するための記述量が多く、読み辛くなってしまうなあと感じたのでそのあたりを工夫していく必要がありそうです。