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

2 comments:

  1. Replies
    1. The project is at: https://github.com/eee-c/Funky-Backbone.js-Calendar

      The master branch only contains the original version of the project. We made branches for each of the chapters in the book.

      The most recent jasmine code is in the jasmine-standalone branch: https://github.com/eee-c/Funky-Backbone.js-Calendar/tree/jasmine-standalone.

      Delete