Thursday, November 24, 2011

Avoiding a Backbone.js + Jasmine Rabbit Hole

‹prev | My Chain | next›

Ugh. While re-organizing my jasmine tests yesterday, I noticed a disappointing hole in testing coverage for my Backbone.js calendar application. Specifically, I am only testing my default route, not navigated routes.


My first instinct was to use the routing specs to test the default behavior (possibly adding one or two more simple descriptions of default behavior). I could then re-purpose the remaining specs to describe specific calendar / appointment behavior. Unfortunately, too much broke when I tried that. Too many tests failed and my first attempts at fixing them only made matters worse.

Ultimately, I think that is how I would like to organize my tests, but to get there, I will need to write some (hopefully) temporary specs to help me transition to that point. So first, I revert my failed refactoring attempt. Then I move all of my existing initial view specs into a self-contained context:
  describe("the initial view", function() {
    beforeEach(function() { /* ... */ });

    describe("the page title", function() { /* ... */ });

    // ...
  });
I am sure to include the previous setup (including HTTP server stubs) in this new scope. This will prevent any context meant for the default navigation from spilling over into my new context.

As for that new context, my setup looks quite similar to what I have done previously except that the one record returned from the /appointments resource needs to be from a specific date in the past:
  describe("navigated view", function() {
    beforeEach(function() {
      // Create the application
      window.calendar = new Cal($('#calendar'));

      // Manually travel back in time to 1999
      Backbone.history.navigate('#month/1999-12', true);

      // doc_list contains one record, couch_doc, which should
      // now be from 1999:
      couch_doc['startDate'] = "1999-10-31";
      couch_doc['title'] = "Party";

      // populate appointments for this month
      server.respondWith('GET', /\/appointments/,
        [200, { "Content-Type": "application/json" }, JSON.stringify(doc_list)]);
      server.respond();
    });
  });
So my setup now creates an instance of my Backbone app, performs a manual navigation to the appropriate month (December 1999 in this case), and establishes the requisite sinon.js server stubs for this date.

As for the actual test, it should be enough to know that the "Party" appointment is listed in my month view:
  describe("navigated view", function() {
    beforeEach(function() { /* ... */ });

    it("should have an appointment", function() {
       expect($('#calendar')).toHaveText(/Party/);
    });
  });
And just like that, it passes:


Ugh.

I mean yay!

I was really headed down quite the rabbit hole before I took this little step back. It is hard to say exactly why things were going so wrong for me. In part, it was because I was testing the wrong thing. Now I am very explicitly testing just the result of manually navigating to a specific month (as a bookmarked push-state URL might do). When I tried to re-purpose my older tests, I was loading the original page, then navigating off and getting double-loading weirdness.

I do think that my app's blank state could be in better shape, but I am not going to worry about that now. If nothing else, my new test verifies that it should not matter -- that both the default and bookmarked start pages work as expected.


Day #215

No comments:

Post a Comment