What is it, naokirin?

JestでMock、Spyを使ってみる

Jestは、JavaScriptのテスティングフレームワークです。

一般的にテスティングフレームワークは、モック機能は別ライブラリとなっていることが多いのですが、Jestには組み込みのモックライブラリが存在しています。もちろん他のモックライブラリを利用することもできますが、Jest本体にあるということでこちらを使うことが多いと思います。

このJestに組み込まれているモックライブラリ周りを整理しておこうと思います。

jestjs.io

Jestのモック

Test Doubleの分類として、Dummy、Fake、Mock、Spy、Stubがあります*1が、Jestではこのように細かい分類で提供されておらず、基本的には jest.mockjest.spyOn を使うことになります。

そのため、Test Doubleのどの分類に該当する場合でも jest.mockjest.spyOn を使ったりします。今回は、ライブラリ時点でTest Double全体の機能提供としてAPIが設計されているため、分類をベースとせず、機能の説明だけをしていきます。関数で説明していますが、インスタンスなども同様です。

関数の戻り値を置き換える

jest.mockjest.spyOn のどちらでも可能です。

// jest.mock の場合
import { target } from 'targetModule';
jest.mock('targetModule');

(target as jest.Mock).mockReturnValue(1);
// jest.spyOn の場合
import * as targetModule from 'targetModule';
const spy = jest.spyOn(targetModule, 'targetFn').mockReturnValue(1);

関数の呼び出しがされていることを検証する

jest.mockjest.spyOn のどちらでも可能です。

// jest.mock の場合
import { target } from 'targetModule';
jest.mock('targetModule');
let mockObj = (target as jest.Mock);

// mock.calls に呼び出されたときの引数の情報を取得できる
// length にすれば呼び出し回数となる
expect(mockObj.mock.calls[0]).toEqual(1);

// mock.results に呼び出されたときの出力の情報を取得できる
// value に戻り値が入る
// type に正常終了かどうかなどが入る
expect(mockObj.mock.results[0].value).toEqual(2);

// expectの場合、呼び出し回数のチェックなどは関数が存在する
expect(mockObj).toHaveBeenCalledTimes(1);
// jest.spyOn の場合
import * as targetModule from 'targetModule';
const spy = jest.spyOn(targetModule, 'targetFn');

// mockと同じ
expect(spy.mock.calls[0]).toEqual(1);
expect(spy.mock.results[0].value).toEqual(2);

テストコード側で注入できる関数の呼び出しチェックをしたい

ここまでと同じように jest.mockjest.spyOn でもある程度できるものの、シンプルに jest.fn でも達成できます。

const mockFn = jest.fn((a, b) => a + b);
func(mockFn);

expect(mockFn).toHaveBeenCalledTimes(1);

テスト独自の実装におきかえたい

jest.mockjest.spyOn のどちらの場合にも使えます。

import * as targetModule from 'targetModule';
const spy = jest.spyOn(targetModule, 'targetFn');

spy.mockImplementation((a, b) => a + b);

クラスの一部の関数だけをモック化したい

クラスの一部の関数のみをモックにしたい場合、 prototype に対して行えば、可能です。

import TargetClass from 'targetClass';

jest.spyOn(TargetClass.prototype, 'targetFn');

モックの呼び出し情報、インスタンス化情報を消す

mockClear を使います。

jest.mockjest.spyOnjest.fn どれでも使えます。

mockObj.mockClear();

expect(mockObj.mock.calls.length).toBe(0);
expect(mockObj.mock.instances.length).toBe(0);

モックの設定も含めてリセットしたい

mockRest を使います。

mockClear で削除される情報に加え、 mockImplementationmockReturnValue による設定もリセットされます。

mockObj.mockReset();

オリジナルの実装に戻す

mockRestore を使います。

こちらは、jest.spyOn でのみ利用可能です。

spy.mockRestore();

まとめ

JestのMockは分類ベースでAPIが提供されていないものの、やりたいことに対してであれば比較的シンプルかなと思います。

ただし注意点として、 mockRestore が使えるのは spyOn だけなので、この点は非常に大きな違いかなと思います。

また、他のテストケースに影響を与えないために、テストケース終了時などに mockClearmockResetmockRestore は適切に呼び出す必要がありそうです。

*1:これはxUnit Test Patternsによる定義をもとにしています。