JavaScript BDD/Testing / Jasmine 2.0 - Spy

前回簡単にまとめたんですが、そのとき書いてなかった Jasmine 2.0の Spy関連について。

Spyって何?

Jasmineの Spyを使うとこんな事が出来ます。

  • 関数呼び出しの追跡(呼び出し引数をあとで確認、呼び出し回数をカウント等)
  • 関数呼び出しを偽装(実際に実行しない、別の関数を実行、例外を投げる等)
  • モックの作成

基本的な使い方

関数呼び出しの追跡

関数呼び出しに関する追跡を行います。監視する関数の動作に影響をあたえません。

function Clazz() {
}
Clazz.prototype.method = function (str) {
  console.log(str);
};

describe("関数呼び出しの追跡", function() {
  var hoge;

  beforeEach(function() {
    hoge = new Clazz();

    // hoge.method() 呼び出しを追跡する //
    spyOn(hoge, 'method')<strong>.and.callThrough();</strong>

    hoge.method('hoge');
    hoge.method('hehe');
  });

  it("methodが呼びだされた事が確認できます。", function() {
    expect(hoge.method).toHaveBeenCalled();
  });

  it("関数呼び出し時の引数がわかります。", function() {
    expect(hoge.method).toHaveBeenCalledWith('hehe');
    expect(hoge.method).toHaveBeenCalledWith('hoge');
  });

  it("関数呼び出し時の引数、呼び出し順序も確認したい場合はこんな感じ", function() {
    expect(hoge.method.calls.argsFor(0)).toEqual(['hoge']);
    expect(hoge.method.calls.argsFor(1)).toEqual(['hehe']);
  });

  it("関数呼び出し回数は2回", function () {
    expect(hoge.method.calls.count()).toBe(2);
  });
});

spyOn(obj, name);で obj.nameの関数を spy(関数)と置き換える事で追跡が可能になります。

例にある spyOn(object, method_name).and.callThrough();で、最後の.and.callThrough()は元関数を呼び出すために必要な設定です。これが無い場合はspyは元の関数を呼び出しません。

注意として引数の確認 toHaveBeenCalledWithは 呼び出し順序は無視して比較します。上の例で呼び出しは’hoge’→’hehe’ですが、expectの順序は関係なく’hehe’→’hoge’でも成功となります。

順序も含めて確認したい場合はspyの callsプロパティの argsFor()を使います。

例には含めていませんが、関数が呼ばれた時のthisも確認できます。この場合 .calls.all(), .calls.first(), .calls.mostRecen() を使う。

関数呼び出しの偽装

「関数呼び出しの追跡」時に実際に実行する関数をすり替えます。

すり替える内容によって spyOn()が少し変わります。

// hoge.method() 呼び出しを追跡するだけで 実際にmethodを実行しない。 //
spyOn(hoge, 'method');

// hoge.method() 呼び出しを追跡し、呼び出しの戻り値は'hoge'にする。 //
spyOn(hoge, 'method').and.returnValue('hoge');

// hoge.method() 呼び出しを追跡し、関数の呼び出しをfake_methodにすり替える //
spyOn(hoge, 'method').and.callFake(function fake_method() {return false;});

// hoge.method() 呼び出しを追跡し、関数が呼び出されると Errorを throwする //
spyOn(hoge, 'method').and.throwError("error message");

特記として、spyに対して.and.stub()を行う事で デフォルトのstub関数(実際の関数を呼び出さない)に戻す事が出来る。

たとえばspyOn(hoge, 'method').and.callThrough();とした後でhoge.method.and.stub();とすると、

その後のspy関数の呼び出しは実際の関数を呼び出さなくなります。

モックの作成

Spyされた関数を持つモックを作るときには、jasmine.createSpyObj(); を呼び出します。

var mock = jasmine.createSpyObj('mock', ['doThat', 'doThis', ...]);
mock.doThat.and.throwError("I hate to do that.");
mock.doThis.and.returnValue("done.");

mock.doThatや doThisは spyなので 呼び出しがすべて記録されます。

その他

追跡情報をクリアする calls.reset()、spy関数を作成する jasmine.createSpy()があります。resetは使った事ありませんが、createSpyは関数のテストでcallback関数を作成する場合に便利です。