Having thought about, some of my Jasmine testing woes may be due, in part, to testing too much. Ultimately, I would like to test that a couple of clicks can open an edit dialog and save changes in my Backbone.js application.
This is not working. Since I am exercising such a large swath of my app, I am unsure where the breakdown is occurring. So instead of testing that I can update the screen via an edit dialog, (which updates the model, which updates the screen):
it("can edit appointments through an edit dialog", function() {
$('.appointment', '#2011-09-15').click();
$('.ok', '#dialog').click();
server.respondWith('PUT', '/appointments/42', '{"title":"Changed!!!"}');
server.respond();
expect($('#2011-09-15')).toHaveText(/Changed/);
});
Instead I will test that updating the model results in a change on the screen: it("displays model updates", function () {
var appointment = Appointments.at(0);
appointment.save({title: "Changed"});
server.respondWith('PUT', '/appointments/42', '{"title":"Changed!!!"}');
server.respond();
expect($('#2011-09-15')).toHaveText(/Changed/);
});
That fails:It is still failing, but I have a much better idea of where things are going wrong. Either the
server.responseWith()
is not returning the results in a recognizable format or...Oh man!
Or I am not updating the view in response to model changes. By trying to implement the edit-dialog feature in one fell swoop, I completely forgot to bind the View to model change events. All I need to do is re-render the appointment view when an appointment model emits a "change" event:
window.AppointmentView = Backbone.View.extend({
template: _.template($('#calendar-appointment-template').html()),
initialize: function(options) {
this.container = $('#' + this.model.get('startDate'));
options.model.bind('destroy', this.remove, this);
options.model.bind('error', this.deleteError, this);
options.model.bind('change', this.render, this);
},
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
this.container.append($(this.el));
return this;
},
// ...
});
With that, my new, smaller test passes:Gah! I made myself crazy over that end-to-end test and I was just missing a tiny bit of track. I think I may have learned me a valuable lesson here today. It is possible to test high-level and low-level in Jasmine. Make damn sure you know which you ought to be doing.
At any rate, I can now go back to my original, end-to-end test:
it("can edit appointments through an edit dialog", function() {
$('.appointment', '#2011-09-15').click();
$('.ok').click();
server.respondWith('PUT', '/appointments/42', '{"title":"Changed!!!"}');
server.respond();
expect($('#2011-09-15')).toHaveText(/Changed/);
});
I can make that test pass my misappropriating the "create" method on my "Application View": window.AppView = Backbone.View.extend({
el: $("#dialog").parent(),
events: {
'click .ok': 'create'
},
create: function() {
// Edit -- the first appointment...
var appointment = Appointments.at(0);
appointment.save({title: $('input.title', '#dialog').val()});
return;
// Actual create code used to be here...
}
});
With that, I have all of my tests passing:That is a pretty useless edit dialog (and it breaks the create dialog), but it does make my test pass. I will claim that as a moral victory.
I think that is a good stopping point for tonight. I revert my silly edit-inside-the-create dialog code. This leaves me with a failing test as a reminder where to pick up tomorrow. All in all, I am in much better shape than when I started. i have a new, focused, passing test describing screen updates after a model change. Even better, I know that I can get end-to-end testing working -- I just need a specialized edit dialog. Tomorrow.
Day #132
No comments:
Post a Comment