Jestのモック関数実装

Javascriptのモジュール形式はESMに移行しつつありますが、テストツールのJestはESMのコードにモックを注入できません。

バンドラーがESMとcommonJSの両形式に対応しているのであれば、差し替えたい関数を含むモジュール(ファイル)のみcommonJSで実装することでモック実装可能です。

モック対象の関数をcommonJSで実装

ESMのexport function some_fn() { ... }に対応するcommonJSのエクスポートは以下のように記述します。

module.exports = {
    some_fn: function () {
        return "some meaningful result";
    }
}

バンドラーがcommonJSに対応していればこのモジュールは、インポート側でimport { some_fn } from './some_fn.js'の形式でインポートできます。

テストコード内のモック実装

commonJSでエクスポートしたモジュールであれば、jest.spyOn()で差し替え可能です。
spyOn()の第1引数はrequireしたモジュール、第2引数は関数名です。

spyOn()でモックに失敗した場合には、テスト実行時に比較的参考になるエラーが表示されます。
エクスポートしたモジュール構造と一致していない場合が多いでしょう。

  const mod = require('./some_fn.js');
  const spy = jest.spyOn(mod, 'some_fn').mockImplementation(
      () => {
        return "mocked result";
      });

mockImplementation()内には、テスト時に実行したいモック関数を実装できます。

モック関数のインスペクション

spyOn()関数などは実装を差し替えるとともに モック関数オブジェクトを返します。上の例では、spyにモック関数を格納しています。

テストケース実行後に、spy.mock.callsプロパティにはケース内で呼び出された履歴が入っています。
他のテストメトリクスと同様、expect()で回数や引数をインスペクトできます。

テストケースごとに異なる値を返す例

テスト前のセットアップではspyOn()で止めておき、各テストケースで実装すると、テストケースごとに異なるモックを利用できます。

以下の例では、ブラウザの言語設定を差し替えています。mockReturnValue()で実装すると戻り値を定義できます。

let lang;

beforeEach(() => {
    lang = jest.spyOn(window.navigator, 'languages', 'get');
});

afterEach(() => {
    jest.clearAllMocks();
});

test("English browser", () => {
    lang.mockReturnValue(['en-US']);
});

test("Japanese browser", () => {
    lang.mockReturnValue(['ja-JP']);
});
中馬崇尋
Chuma Takahiro