Testing model binding in Backbone JS with Jasmine

喜欢而已 提交于 2019-12-12 13:03:34

问题


I have a view that contains a model. The view listens for an event from the model and will perform an action once the event is triggered. Below is my code

window.Category = Backbone.Model.extend({})

window.notesDialog = Backbone.View.extend({
  initialize: function() {
    this.model.bind("notesFetched", this.showNotes, this);
  },
  showNotes: function(notes) {
    //do stuffs here
  }
})

I want to test this using Jasmine and below is my test (which doesn't work)

it("should show notes", function() {
   var category = new Category;

   var notes_dialog = new NotesDialog({model: category})

   spyOn(notes_dialog, "showNotes");
   category.trigger("notesFetched", "[]");
   expect(notes_dialog.showNotes).toHaveBeenCalledWith("[]");
})

Does anyone know why the above test doesn't work? The error I get is "Expected spy showNotes to have been called with [ '[]' ] but it was never called."


回答1:


I was doing something similar where I had a view, but I couldn't get the spy to work properly unless I added it to the prototype, and before I created the instance of the view.

Here's what eventually worked for me:

view.js

view = Backbone.View.extend({
   initialize: function(){
      this.collection.bind("change", this.onChange, this);
   },
   ...
   onChange: function(){
      console.log("Called...");
   }
});

jasmine_spec.js

describe("Test Event", function(){
   it("Should spy on change event", function(){
      var spy = spyOn(view.prototype, 'onChange').andCallThrough()
      var v = new view( {collection: some_collection });

      // Trigger the change event
      some_collection.set();

      expect(spy).toHaveBeenCalled()
   });
});

I would test initially with the toHaveBeenCalled() expectation and change to the toHaveBeenCalledWith() after you get that working...

Update 5/6/2013: Changed update() to set()




回答2:


Try to amend your existing test code as follows:

it("should show notes", function() {
   var category = new Category;

   spyOn(NotesDialog.prototype, "showNotes");
   var notes_dialog = new NotesDialog({model: category})

   category.trigger("notesFetched", "[]");
   expect(notes_dialog.showNotes).toHaveBeenCalledWith("[]");
})

In your original code, the instance of the method you are calling is one defined in the bind closure, whereas the one you are spying on is in the notes_dialog instance. By moving the spy to the prototype, you are replacing it before the bind takes place, and therefore the bind closure encapsulates the spy, not the original method.




回答3:


Using a spy means to replace the function you spying on. So in your case you replace the bind function with the spy, so the internal logic of the original spy will not call anymore. And thats the right way to go cause you dont wanna test that Backbones bind is work but that you have called bind with the specific paramaters "notesFetched", this.showNotes, this.

So how to test this. As you know every spy has the toHaveBeenCalledWith(arguments) method. In your case it should looks like this:

expect(category.bind).toHaveBeenCalledWith("notesFetched", category. showNotes, showNotes)

So how to test that trigger the "notesFetched" on the model will call your showNotes function. Every spy saves the all parameters he was called with. You can access the last one with mostRecentCall.args.

category.bind.mostRecentCall.args[1].call(category.bind.mostRecentCall.args[2], "[]");
expect(notes_dialog.showNotes).toHaveBeenCalledWith("[]");

mostRecentCall.args[1] is the the second argument in your bind call (this.showNotes). mostRecentCall.args[2] is the the third argument in your bind call (this).

As we have test that bind was called with your public method showNotes, you can also call the your public method showNotes directly, but sometimes the passed arguments can access from outside so you will use the shown way.




回答4:


Your code looks fine, except do you have the test wrapped in a describe function, as well as an it function?


describe("show notes", function(){
  it("should show notes", function(){
    // ... everything you already have here
  });
});

Total guess at this point, but since you're not showing the describe function that's all I can think it would be. You must have a describe block for the tests to work, if you don't have one.




回答5:


You are pretty close ;) spyOn replaces the function with your spy and returns you the spy. So if you do:

   var dialog_spy = spyOn(notes_dialog, "showNotes");
   category.trigger("notesFetched", "[]");
   expect(dialog_spy).toHaveBeenCalledWith("[]");

should work just fine!



来源:https://stackoverflow.com/questions/8859148/testing-model-binding-in-backbone-js-with-jasmine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!