web-dev-qa-db-ja.com

Jasmine-コンストラクター内でのメソッド呼び出しのスパイ

Javascriptオブジェクトコンストラクターで次のメソッドが呼び出されるかどうかをテストします。 Jasmineのドキュメントで見たものから、コンストラクタメソッドをスパイでき、オブジェクトがインスタンス化された後にメソッドをスパイできますが、オブジェクトが構築される前にメソッドをスパイすることはできないようです。

オブジェクト:

Klass = function() {
    this.called_method();
};

Klass.prototype.called_method = function() {
  //method to be called in the constructor.
}

私は仕様でこのようなことをしたい:

it('should spy on a method call within the constructor', function() {
    spyOn(window, 'Klass');
    var obj = new Klass();
    expect(window.Klass.called_method).toHaveBeenCalled();
});
60
Levi McCallum

プロトタイプメソッドを直接スパイします。

describe("The Klass constructor", function() {
  it("should call its prototype's called_method", function() {
      spyOn(Klass.prototype, 'called_method');  //.andCallThrough();
      var k = new Klass();
      expect(Klass.prototype.called_method).toHaveBeenCalled();
  });
});
104
Dave Newton

概して、私は上記のデイブ・ニュートンの答えに同意します。ただし、このアプローチには考慮すべきエッジケースがいくつかあります。

別のテストケースを使用して、Daveのソリューションにバリエーションを加えます。

// production code
var Klass = function() {
  this.call_count = 0;
  this.called_method();
};
Klass.prototype.called_method = function() {
  ++this.call_count;
};

// test code
describe("The Klass constructor", function() {
  it("should call its prototype's called_method", function() {
    spyOn(Klass.prototype, 'called_method');
    var k = new Klass();
    expect(k.called_method).toHaveBeenCalled();
  });
  it('some other test', function() {
    var k = new Klass();
    expect(k.call_count).toEqual(1);
  });
});

最初のテストのスパイ設定がテストの境界を越えて2番目のメソッドに保持されるため、2番目のテストは失敗します。 called_methodはcall_countをインクリメントしないため、this.call_countは1に等しくありません。また、誤検知を伴うシナリオを見つけることも可能です。

さらに、スパイが残るため、作成されるKlassインスタンスが増えると、スパイが消費するメモリヒープが大きくなります。これは、スパイがcalled_methodの各呼び出しを記録するためです。ほとんどの場合、これはおそらく問題ではありませんが、念のため、注意してください。

この問題の簡単な解決策は、スパイが使用された後に削除されるようにすることです。少しいように見えるかもしれませんが、次のように機能します。

// test code
describe("The Klass constructor", function() {
  it("should call its prototype's called_method", function() {
    var spy = jasmine.createSpy('called_method');
    var method = Klass.prototype.called_method;
    Klass.prototype.called_method = spy;
    var k = new Klass();
    expect(spy).toHaveBeenCalled();
    Klass.prototype.called_method = method;
  });

[注-最後に少し意見]より良い解決策は、コードをテストしやすくするために、実動コードの作成方法を変更することです。原則として、プロトタイプをスパイすることはおそらく回避すべきコード臭です。コンストラクターで依存関係をインスタンス化する代わりに、それらを注入します。コンストラクターで初期化を行う代わりに、適切なinitメソッドに従います。

11
alecmce