简体   繁体   中英

Testing backbone.js application with jasmine - how to test model bindings on a view?

I had some interesting tribulations in trying to test whether views were correctly bound to events. In backbone, we typically bind to events in the initialize method, using something along the lines of: something.bind("change", this.render); . In my test, I want to make sure that this binding is set up, so I did the following:

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

But, that won't work. Because the bind occurs in MyView's initialize function, the event get's bound to myView's render function AT THAT TIME. So, when you add your spy, it wraps the render function and sets it back into place at myView.render. But the closure created by the first bind still exists, and we are totally hozed. So what can we do about it? What I did, is move my bind call's to a seperate function, something like:

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

and my test then looks like:

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

This works, but I'm looking for a better solution. Thanks

I have managed to achieve this using prototype patching. Before you create the instance of the view, spyOn the constructor's prototype.

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

Instead of spying on the callback you might try spying on something.bind. Then test that bind was called w/ the appropriate arguments. This is working for me so far. I'm using sinon.js instead of jasmine's built-in spies. sinon.js makes it a bit easier to test for args passed to a method call in a stack of same method calls (eg a bunch of calls to bind in a view init). So I haven't tested this idea w/ jasmine alone but believe it should be possible.

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)

And 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

I solved this problem by spying on a function called by my render function. So in your example:

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 */ }
});

test looks like:

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

then I wrote a separate test for whatever someOtherFunction does.

You should consider looking at Sinon.js. You could stub/mock the render() call and not even have to worry about 'someOtherFunction()'.

这可能与Backbone内部结构太紧密相关,但您可以手动检查回调链:

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

I ran into the same problem and changed my Views code from:

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

to:

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

And my jasmine tests worked as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM