Posted at

Jest のモック関数について整理する

More than 3 years have passed since last update.

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);