2011-06-24 11 views
20

私は、ビューがイベントに正しくバインドされているかどうかをテストする際にいくつかの面白い試練がありました。バックボーンでは、通常、initializeメソッドのイベントにバインドします。something.bind("change", this.render);の行に沿って何かを使用します。私のテストでは、このバインディングが設定されていることを確認したいので、次のようにしました:jasmineを使用したbackbone.jsアプリケーションのテスト - ビューのモデルバインディングのテスト方法

this.myView = new MyView(); 
spyOn(this.myView, "render");; 
this.legendView.groupData.trigger("change"); 
expect(this.legendView.render).toHaveBeenCalled(); 

しかし、それは動作しません。バインドはMyViewのinitialize関数で行われるため、イベントgetはmytimeのレンダリング関数AT THAT TIMEにバインドされます。したがって、スパイを追加すると、レンダリング関数がラップされ、myView.renderに戻ります。しかし、最初のバインドによって作成されたクロージャーはまだ存在し、我々は完全にうんざりしています。では、どうすればいいのですか?私は、別々の機能への私のバインド・コールさん、のようなものに移動されました:

myView = Backbone.View.extend({ 
initialize: function(){ 
    _.bindAll(this, "render"); 
    this.initialize_model_bindings(); 
}, 
initialize_model_bindings: function(){ 
    something.bind("change", this.render); 
}, 
render: function(){ //... } 
}); 

をし、私のテストは、その後、次のようになります。これは動作しますが、私はよりよい解決策を探しています

this.myView = new MyView(); 
spyOn(this.myView, "render"); 
this.myView.initialize_model_bindings(); 
this.legendView.groupData.trigger("change"); 
expect(this.legendView.render).toHaveBeenCalled(); 

。ありがとう

+0

あなたはこの点について説明していますか?「最初のバインドによって作成されたクロージャはまだ存在し、私たちは完全に夢中になっていますか?私は同じ問題を抱えており、何が起こっているのか理解しようとしています。ありがとう。 –

+1

'new MyView()'を呼び出すと、initializeメソッドが呼び出され、その時点でオブジェクトに存在するレンダリング関数がchangeイベントにバインドされます。その時点から、変更イベントにバインドされているレンダリング関数にアクセスすることは不可能です。これはクロージャーにロックされています。 – idbentley

+0

説明をありがとう。 –

答えて

5

コールバックを監視するのではなく、something.bindを監視することがあります。次に、bindが適切な引数を指定して呼び出されたかどうかをテストします。これはこれまで私のために働いています。ジャスミンのスパイの代わりにsinon.jsを使用しています。 sinon.jsは、同じメソッド呼び出しのスタック(たとえば、ビューinitでバインドする一連の呼び出し)でメソッド呼び出しに渡されたargをテストするのが少し簡単です。だから私はジャスミン単独でこのアイデアをテストしていないが、それが可能でなければならないと信じている。

spyOn(this.legendView.groupData, 'bind'); 
this.myView = new MyView(); 
expect(this.legendView.groupData.mostRecentCall.args).toEqual('change', this.myView.render); // example!! only works if testing a single call to bind or the last call in a series (ie mostRecentCall) 

そしてW/sinon.js

sinon.spy(this.legendView.groupData, 'bind'); 
this.myView = new MyView(); 
expect(this.legendView.groupData.bind.calledWith('change', this.myView.render); // works w/ any number of calls to bind 
3

私は私のレンダリング機能によって呼び出された関数をスパイすることにより、この問題を解決しました。だからあなたの例では:

myView = Backbone.View.extend({ 
    initialize: function(){ 
     _.bindAll(this, "render"); 
     something.bind("change", this.render); 
    }, 
    someOtherFunction: function(){}, //this function only called from render 
    render: function(){ this.someOtherFunction(); /* rest of render function */ } 
}); 

テストは、次のようになります。

this.myView = new MyView(); 
spyOn(this.myView, "someOtherFunction"); 
this.myView.something.trigger("change"); 
expect(this.myView.someOtherFunction).toHaveBeenCalled(); 

その後、私はない何someOtherFunctionための別のテストを書きました。

+0

これはひどい解決策ではないようですが、私のレンダー機能がかなり頻繁に変化することがわかりました。私がこれをしたら、私の仕様をかなり更新しているような気がする。 – idbentley

2

Sinon.jsを見ることを検討する必要があります。あなたはrender()呼び出しをスタブ/モックすることができ、 'someOtherFunction()'について心配する必要はありません。

1

これはあまりにも密接にバックボーンの内部に結合することができますが、手動でコールバックチェーンを確認することができます。

expect(this.legendView.groupData._callbacks['change']).toContain(this.myView.render) 
12

私はこの使用して、プロトタイプのパッチを達成するために管理しています。ビューのインスタンスを作成する前に、コンストラクタのプロトタイプspyOnを作成してください。

spyOn(MyView.prototype, 'changeSelected'); 
var view = new MyView(); 
view.selectSomething(); 
expect(view.changeSelected).toHaveBeenCalled(); 
+0

何らかの理由で、これは 'sinon.spy(...)'を使ってのみ私のために働いていました。このヒントありがとう! – fifigyuri

+0

昨年、サイロンのアピが変わってしまったら驚かないだろう... – mjtamlyn

0

私は同じ問題に遭遇してから、私のビューのコードを変更:に

this.model.on('change', this.render, this); 

this.model.on('change', function() { 
    this.render(); 
}, this); 

そして予想通り、私のジャスミンのテストが働いていました。

関連する問題