Mox
Mox

Reputation: 19

I'm new to TDD backbone with jasmine-sinon. change event in backbone unit test

I'm making a test for event change to a selectbox view in backbone which is 'should provide the correct path to router.navigate when changed'. this is the scenario, If I select a value in the dropdown, it should redirect to the correct url.

this is the test (campaign.test.js):

it('should provide the correct path to router.navigate when changed', function() {
    var routeName = 'test_campaign';
    var routerSpy = sinon.spy(Insights.router, 'navigate');

    this.view.$el.bind('change', function() {
      expect(routerSpy).toHaveBeenCalledWith('campaign/' + routeName, true);
    });

    //create the option element holding the test value
    this.view.$el.append($('<option value="' + routeName +'" selected="selected" />'));

    this.view.$el.trigger('change');


    routerSpy.restore();
  });

this is the module (campaign.js):

DropdownView: Backbone.View.extend({
  tagName: 'select',
  className: 'campaign-list',
  events: {
     'change' : 'navCampaign'
  },


  initialize: function() {
    _.bindAll(this);
    this.collection.on('reset', this.render);
    this.collection.on('add', this.addCampaign);

    //if no campaigns exist then add a dummy placeholder
    if(this.collection.models.length === 0) {
      this.$el.append('<option value="">No Campaigns</option>');
    }

  },


  navCampaign: function() {

    Insights.router.navigate();
  },


  addCampaign: function(campaign) {
    //remove the No Campaigns placeholder when campaigns are added
    var firstChild = this.$el.children(':first');
    if(firstChild.attr('value') === '') {
      firstChild.remove();
    }

    var view = new Insights.Campaign.DropdownItem({ model: campaign });
    var item = view.render().el;
    this.$el.append(item);
  },

  render: function() {
    this.collection.each(this.addCampaign);
    return this;
  }
})

In my test I created a new OPTION element then set a value with attribute selected.

How can I pass this as the currentTarget of the change event and send it to trigger()?

Or is there a easier way to do this?

Im getting this test fail. Error: Expected Function to have been called with 'campaign/test_campaign', true.

Upvotes: 0

Views: 787

Answers (3)

Mox
Mox

Reputation: 19

sorry, I would like to make this clear with the help of my team mate.

var routeName = this.view.$el.val(); is pointing to null

by making the value dynamic it will loose the test's effectiveness,

so I put back to var routeName = 'test_campaign'.

the point of this test is to specify what value it is expecting based on a predefined set of inputs.

Upvotes: 0

Mox
Mox

Reputation: 19

Here are the changes, that made this test passed. :)

I changed the value of variable routeName, in campaign.test.js

from

 var routeName = 'test_campaign';

to

 var routeName = this.view.$el.val();

Then implemented this test to campaign.js

navCampaign: function() {
    var newRoute = 'campaign/' + this.$el.val();
    Insights.router.navigate(newRoute, true);
  }

there I got this green. :)

Upvotes: 1

Andreas K&#246;berle
Andreas K&#246;berle

Reputation: 111042

Your test have to look like this:

  it('should provide the correct path to router.navigate when changed', function() {
    var routeName = 'test_campaign';
    var routerSpy = sinon.spy(Insights.router, 'navigate');
    var view = new DropdownView();

    //create the option element holding the test value
    this.view.$el.append($('<option value="' + routeName +'" selected="selected" />'));

    this.view.$el.trigger('change');
    expect(routerSpy).toHaveBeenCalledWith('campaign/' + routeName, true);

    routerSpy.restore();
  });

First of all you have to create an instance of your view in the test and you have to create the spy before you create the view. Both can also be done in the beforeEach block.

When you adding the expect code in an event handler it can happen that it will called before the navigate method on your router is called. Cause two handlers are added to the listener, one to call navigate on the router and one that test that it was called on the router. As you can't insure which one is called first the test will fail when the listener from the test is called first.

Maybe its better to test it with passing the collection with data to the view, not setting the DOM of the view element directly in the test. So you will also test that your initialize and addData method will work.

Upvotes: 1

Related Questions