Testing views

Views manage the relationship between data (such as, models or collections) and the user interactions (DOM). In the case of views, you should test for the following:

  • Rendering: Given a model or collection, you should verify that the output HTML is the right one
  • Events: This verifies that the DOM events are handled correctly
  • Model changes: If the model changes something, the view should be in sync

For this example, we are going to test the ContactForm view; the responsibility of this view is to show a form to the user and then get the user input to update a model.

When making test on views, it is recommended to use a fake model and not the original Contact model. The main reason for this is to isolate the ContactView object so that if a test fails, you will know that the error is isolated in the view and does not depend on the Contact model.

You can start testing whether the rendered HTML is right, as follows:

var Backbone = require('backbone');
var ContactForm = require('../../../../app/js/apps/contacts/views/contactForm');

describe('Contact form', () => {
var fakeContact;

beforeEach(() => {
fakeContact = new Backbone.Model({
name: 'John Doe',
facebook: 'https://www.facebook.com/john.doe',
twitter: '@john.doe',
github: 'https://github.com/johndoe',
google: 'https://plus.google.com/johndoe'
    });
  });

it('has the rigth class', () => {
var view = new ContactForm({model: fakeContact});
expect(view.className).toEqual('form-horizontal');
  });

it('renders the rigth HTML', () => {
var view = new ContactForm({model: fakeContact});

view.render();

expect(view.$el.html()).toContain(fakeContact.get('name'));
expect(view.$el.html()).toContain(fakeContact.get('twitter'));
expect(view.$el.html()).toContain(fakeContact.get('github'));
expect(view.$el.html()).toContain(fakeContact.get('google'));
expect(view.$el.html())
.toContain(fakeContact.get('facebook'));
  });
});

Note how in the test, we are looking in the output HTML if contains a specific text on it. You can use specific selectors instead:

expect(view.$el.find('#name').val())
.toContain(fakeContact.get('name'));

However, it is not recommended to do this in unstable applications as the design can quickly change and the tests will fail even if the name is on the screen:

Testing views

Figure 8.1 Jasmine testing functions

Figure 8.1 illustrates the relationship between the beforeEach(), afterEach(), and it() functions. When you define one or more beforeEach() functions in describe(), then all the beforeEach() functions will always be executed before the it() functions. This feature is very useful as you can ensure the same initial conditions for each test.

In the example test suite for the ContactForm object, we are ensuring that fakeContact always has the same attributes; if you change something in the model under an it() function, the next function will always get a clean fakeContact model to test.

The ContactForm object has a Save button that triggers a form:save event when it is clicked; to test this, you can listen for the event on a Jasmine spy function. A spy function is a function that does nothing but record when and how it is called. Then, you can use it to make expectation in it:

it('triggers a form:save event when save button is cliecked', () => {
var view = new ContactForm({model: fakeContact});
var callback = jasmine.createSpy('callback');

view.on('form:save', callback);
view.render();

// Emulate a user click
view.$el.find('#save').trigger('click');

expect(callback).toHaveBeenCalled();
});

The createSpy() method of Jasmine creates a spy function that will be used as the event handler for the from:save event. Then, it emulates a click event on the save button and tests whether the callback function was called.

We can go a step forward and check whether the function is called with the model as argument:

expect(callback).toHaveBeenCalledWith(mockContact);

Now is time to test when the user makes the input in the form and then click the Save button; what we are expecting from it is that the model changes with the input values:

it('updates the model when the save button is clicked', () => {
var view = new ContactForm({model: fakeContact});
var callback = jasmine.createSpy('callback');
varexpectedValues = {
name: 'Jane Doe',
facebook: 'https://www.facebook.com/example',
twitter: '@example',
github: 'https://github.com/example',
google: 'https://plus.google.com/example'
  };

view.on('form:save', callback);
view.render();

  // Change the input fields
  view.$el.find('#name').val(expectedValues.name);
view.$el.find('#facebook').val(expectedValues.facebook);
view.$el.find('#twitter').val(expectedValues.twitter);
view.$el.find('#github').val(expectedValues.github);
view.$el.find('#google').val(expectedValues.google);

  // Emulate a change events on all input fields
view.$el.find('input').trigger('change');

  // Emulate a user click
view.$el.find('#save').trigger('click');

// Get the argument passed to the callback function
var callArgs = callback.calls.argsFor(0);
var model = callArgs[0];

expect(model.get('name')).toEqual(expectedValues.name);
expect(model.get('facebook')).toEqual(expectedValues.facebook);
expect(model.get('twitter')).toEqual(expectedValues.twitter);
expect(model.get('github')).toEqual(expectedValues.github);
expect(model.get('google')).toEqual(expectedValues.google);
});

In this test, we are changing the values in the input fields and then clicking the save button in the form. The callback spy function records how the form:save event is triggered and extracts the argument passed to it. We can use this argument to test whether the model was updated as expected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.176.72