Wednesday, November 23, 2011

Testing Backbone.js Routing with Jasmine

‹prev | My Chain | next›

Yesterday I had me a big breakthrough in my Backbone.js testing with jasmine. By replacing one, tiny snippet of non-idiomatic routing code with the "backbone way" equivalent, I magically eliminated a number of annoyances.

In particular, I now have tests failing when I intentionally break my application's routing. Unfortunately, the failure message is non-obvious. The failure indicates that appointments are not being populated on my calendar application. In fact, they are not being populated because the routing has failed, but it will take a bit of digging each time I cause this failure.

So today, I hope to test things a little more directly. Given the I am creating the application in my spec setup:
  beforeEach(function() {
    // ...
    window.calendar = new Cal($('#calendar'));
    // ...
  });
Then, in my tests, I can check to see that the application auto-redirects to the current month:
describe("routing", function() {
    it("defaults to the current month", function() {
      var today = new Date()
        , year = today.getFullYear()
        , m = today.getMonth() + 1
        , month = m<10 ? '0'+m : m;

      expect(Backbone.history.getFragment())
        .toEqual("month/" + year + "-" + month);
    });
  });
That passes, but, when I remove the true second argument to Backbone.history.navigate() in the application's default route:
  var Routes = Backbone.Router.extend({
    // ...
    _setDefault: function() {
      console.log("[setDefault]");
      var month = Helpers.to_iso8601(new Date).substr(0,7);
      Backbone.history.navigate('#month/' + month); //, true);
    },
    // ...
  });
Unfortunately, that does not fail. Ah, of course... I am testing that the default route does something. What I try to break is the subsequent triggering the appropriate route. So the default route is firing, but is not triggering the applicable route.

For that, I need to add a secondary test to verify that default route sets the application date in addition to redirecting:
  describe("routing", function() {
    it("sets the date of the appointment collection", function() {
      var appointments = window.calendar.appointments;
      expect(appointments.getDate())
         .toEqual("2011-11");
    });
  });
With, that, I get my desired failure:


I can then replace the true argument to ensure the subsequent route is triggered and now my test passes:


If you cannot make a test fail by changing one line (or even one word), then the test is useless. I was a bit worried at first about the initial passing test. Once I realized I was testing two different things (the route firing and the route triggering a subsequent route), I got a failing test. I now feel much better about my test suite.

Failure: it's a good thing when testing.


Day #214

No comments:

Post a Comment