Wednesday, September 14, 2011

Simple End-to-End Jasmine Testing of Backbone.js

‹prev | My Chain | next›

I am fairly confident that I have made a mess out of adding and editing appointments in my Backbone.js calendar. The UI for adding appointments is somehow split between two Backbone views. The "application" view adds handlers to a jQuery UI dialog for creating new appointments. But a "day" view, attached to each day in the calendar, is responsible for opening that jQuery UI dialog. And don't even get me started on what I did to the edit dialog.

Yup, it's a fantastic mess, but... I have a secret weapon to help me fix said mess: Jasmine tests. And not just any jasmine tests—end-to-end jasmine tests. Click this button, the dialog opens, fill in that text, hit submit and voilĂ  a new appointment shows up in the UI. Best of all, since none of that relies on the underlying implementation, I ought to be able to fix everything without changing the test. The test will serve as a sanity check as I refactor.

Yup, that'll be nice. But first I actually need tests for the add dialog (I have tests for delete and the overall calendar only):
Now to add some tests for the add dialog itself:

  describe("adding an appointment", function() {
    it("sends clicks on day to an add dialog", function() {
      $('#2011-09-14').click();

      var dialog = $('#dialog').parent();
      expect(dialog).toBeVisible();
      expect(dialog).toHaveText(/Add/);
    });

    it("displays the date clicked in the add dialog", function() {
      $('#2011-09-14').click();
      expect($('#dialog')).toHaveText(/2011-09-14/);
    });
  });
Aside from some funkiness due to the title of a jQuery UI dialog being in the parent of the "#dialog" element, both of those two specs are straight forward and they pass.

Last up, I need to verify the round trip, stubbing out the XHR request with sinon.js:

  describe("adding an appointment", function() {
    it("sends clicks on day to an add dialog", function() { /* ... */ });

    it("displays the date clicked in the add dialog", function() { /* ... */ });

    it("adds a new appointment to the UI when saved", function() {
      $('#2011-09-14').click();
      $('.ok').click();

      var appointment = {
        "id": "42",
        "rev": "1-2345",
        "startDate": "2011-09-14",
        "title": "Groovy meeting",
        "description": "asdf"
      };

      server.respondWith('POST', '/appointments', JSON.stringify(appointment));
      server.respond();

      expect($('#2011-09-14')).toHaveText(/Groovy/);
    });
  });
Wow. I have a hard time I got that newest test right. As a quick sanity check, I intentionally break it:
    it("adds a new appointment to the UI when saved", function() {
      // ...
      expect($('#2011-09-14')).toHaveText(/Grooy/);
    });
And yup, it does break:
Cool. Safe in the knowledge that I finally have my add dialog accurately described by jasmine, I call it a night. Up tomorrow: I hack together a pretty ugly edit implementation.


Day #133

0 comments:

Post a Comment