5
 onSaveEvent: function (event) {
                if (this.model !== null) {
                    var that = this;

                    this.model.save(this.model.toJSON(), {
                        success: function (model) {
                            that.model = model;
                            that.model.attributes.isDirty = false;
                        },

                        error: function (model, xhr) {
                            that.model.attributes.isDirty = true;
                        }
                    });
                }
            }
        }

how to unit test the model's save' success and error responses in Jasmine?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Gururaj
  • 1,115
  • 2
  • 14
  • 17

4 Answers4

2

To test this without a fake server you can test that the function was binded to the model an then call the binded function by yourself. In other words mock out the ajax save part out of the model.

var view = new YourView()
jasmine.spyOne(view.model, 'save')
view. onSaveEvent()
var args = view.model.save.mostRecentCall.args

args[1].success()
expect(view.model.attributes.isDirty).toBeFalsy()

args[1].error()
expect(view.model.attributes.isDirty). toBeTruthy()
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
1

You can use Sinon.js to create a fake server for your tests.

http://sinonjs.org/

Example code:

  describe("when saving a user model", function() {

    beforeEach(function() {
      this.server = sinon.fakeServer.create();
      this.responseBody = '{"name":"test user","id":1,"title":"tester"}';
      this.server.respondWith(
        "POST",
        "/user",
        [
          200,
          {"Content-Type": "application/json"},
          this.responseBody
        ]
      );
      this.eventSpy = sinon.spy();
    });

    afterEach(function() {
      this.server.restore();
    });

    it("should not save when name is blank", function() {
      this.user.bind("error", this.eventSpy);
      this.user.save({"name": ""});

      expect(this.eventSpy).toHaveBeenCalledOnce();    
      expect(this.eventSpy).toHaveBeenCalledWith(this.user, "cannot have a blank name");
    });

    it("should call the server", function() {
      this.user.save();
      expect(this.server.requests[0].method).toEqual("POST");
      expect(this.server.requests[0].url).toEqual("/user");
      expect(JSON.parse(this.server.requests[0].requestBody)).toEqual(this.user.attributes);
    });

  });
Scott Puleo
  • 3,684
  • 24
  • 23
0

You need sinon to emulate server responses. This library has utilities like this:

 this.server.respondWith("GET", "/episode/123",
      [200, {"Content-Type": "application/json"},
      '{"id":123,"title":"Hollywood - Part 2"}']);

So whenever you have a Model with the root episode and id 123, sinon will return this on a fetch call.

Read this: http://tinnedfruit.com/2011/03/03/testing-backbone-apps-with-jasmine-sinon.html

Update: Adding a second workaround as asker request. Mocking save method.

//Use this in your tests instead of Backbone.js Model

var ExtendedModel = Backbone.Model.extend({
    //mocked save:
    save : function(data, options){
         if( data ){
             this.set(data);
         }
         var mocked = this.toJSON();
         if( !mocked.id ){
             mocked.id = new Date().getTime();
         } 
         mocked = this.parse(mocked);
         if( options.success ){
             options.success(this);  
         } 
         if( options.error ){
             options.error(this);  
         } 
    }
});

var MyModel = ExtendedModel.extend({
});

However I'm still suggest you to use Sinon. Mocking the Backbone.js is not ellegant and also support header response codes and other stuff is also more complex and a kind of reinvent the wheel. While with sinon you just need to add the library an create server response.

Daniel Aranda
  • 6,426
  • 2
  • 21
  • 28
  • I am using the following technologies backbone.js, require.js, jquery, Rest services over WebAPI. from jasmine, I am mocking the model object and binding to a view for unit testing. when I get the response from the view to the mock model, how to unit the model's save (sucess, error) responses. isn't there a way to unit test the model responses without sinon. – Gururaj May 06 '13 at 02:52
  • Sinon is just other JavaScript lib, not sure why you cannot add it. But anyway, other workaround is to overwrite save method. I will add that workaround in my answer. – Daniel Aranda May 06 '13 at 13:47
0

I'm not sure I like going through sinon here, afterall the ajax call is made by backbone, not by the unit you are testing, here is my solution

    var Model = Backbone.Model.extend({
      success_callback : function (model){
        this.model = model;
        this.model.attributes.isDirty = false;

      },
      error_callback: function (model, xhr){

      },
      onSaveEvent: function (event) {

        this.save([], {
            success: _.bind(this.save_success, this),
            error: _.bind(this.error_callback, this);
        });
    });


    var callback_invoker = function(type_str, which_argument) {
      this.which_argument = which_argument || 0;
      this.type_str = type_str;
      var func = function() {
        var options = arguments[this.which_argument] || {};
        if (this.type_str == 'success') {
          var run = options.success || function() {};

        }else if (this.type_str == 'error') {
          var run = options.error || function() {};
        }
        run();
      };
      this.function = _.bind(func, this);
    };

    it('save success calls callback', function() {
      instance = new Model();
      spyOn(instance, 'save_success');
      spyOn(_,'bind').andCallThrough();
      var invoker = new callback_invoker('success', 1);
      spyOn(instance, 'save').andCallFake(invoker.function);
      instance.onSaveEvent();
      expect(_.bind).toHaveBeenCalledWith(instance.save_success, instance);
      expect(instance.save_success).toHaveBeenCalled();
    });