Jestを使ったテストを行うとき、 Mock Functions を駆使してテストすることになる。
ここでは公式サイトの記述を少し意訳しながらまとめておく。
意訳の狙いは、次の二つ。
- テストコードを書くときに、なぜ mock function が必要かということがわかるように
- テストコードを読むときに登場する mock function に混乱しないように
mock functions
mock function は、あるモジュールとそれ以外のモジュール(あるモジュール内の関数が、APIとして使いたい関数が定義されている)との関連性をテストする時に、テストの複雑さを軽減するために使われる。
mock functions は今テストしない側の関数呼び出しやコンストラクタ呼び出しをキャプチャするので、テストしたい関数が正しくAPIを使っているかを、実際のAPI、モジュールがなくても検証できるようになる。そして、これらのAPIについてテスト時の戻り値を制御することができる。つまり今やりたいテストに集中し、その他を簡略化するようなテストコードを記述できるようになる。
また、大きく2つの方法で mock function を生成することができる。
-
require()
ing a mocked components - explicitly requesting one from
jest.genMockFunction()
// The function was called exactly once
expect(someMockFunction.mock.calls.length).toBe(1);
// The first arg of the first call to the function was 'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe('first arg');
// The second arg of the first call to the function was 'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe('second arg');
.mock property
mocked function がどのように呼び出されてきたかを示すデータを .mock
プロパティに持っている。
.mock
プロパティはコンテキストthis
の値も追跡しているので、それを検証することもできる。
var myMock = jest.genMockFunction();
var a = new myMock(); // myMock関数内の this は a
var b = {};
var bound = myMock.bind(b); // myMock関数内の this は b
bound();
console.log(myMock.mock.instances);
> [ <a>, <b> ]
Mock Return Values
Mock function は、テスト用の値を inject することもできる。リアルに値を計算しなくても直接戻り値を指定することができるので、関数を引数に取る関数をテストするときも便利。この仕組みを使うと、本物同等の複雑なスタブを用意する必要がなくなる。この仕組みを駆使して、テストに関係ない関数のロジックを実装しないことがコツとなる。
var myMock = jest.genMockFunction();
console.log( myMock() );
> undefined
myMock.mockReturnValueOnce(10)
.mockReturnValueOnce('x')
.mockReturnValue(true);
console.log(myMock(), myMock(), myMock(), myMock());
> 10, 'x', true, true
Mock Implementations
チェーンするような関数の mock が必要な場合は、次のようなシンタックスシュガーが使える。
var myObj = {
myMethod: jest.genMockFunction().mockReturnThis()
};
// is the same as
var myObj = {
myMethod = jest.genMockFunction().mockImplementation(function() {
return this;
});
};
Custom Matchers
assert がより簡潔に記述できるように、 custom mathchers が用意されている。これらは基本的に .mock property を使って新たな関数を定義しているだけなので、必要に応じて自作することもできる。
//Cutome mathchers
// The mock function was called at least once
expect(mockFunc).toBeCalled();
// The mock function was called at least once with the specified args
expect(mockFunc).toBeCalledWith(arg1, arg2);
// The last call to the mock function was called with the specified args
expect(mockFunc).lastCalledWith(arg1, arg2);
// Using plain mock functions APIs to expect something
// The mock function was called at least once
expect(mockFunc.mock.calls.length).toBeGreaterThan(0);
// The mock function was called at least once with the specified args
expect(mockFunc.mock.calls).toContain([arg1, arg2]);
// The last call to the mock function was called with the specified args
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual(
[arg1, arg2]
);
// The first arg of the last call to the mock function was `42`
// (note that there is no sugar helper for this specific of an assertion)
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42);