Saturday, December 10, 2011

Jasmine Testing of a Require.js Powered Backbone App

‹prev | My Chain | next›

Over the last few days, I have made good progress converting my Backbone.js application to require.js. During that time, I have not paid the slightest bit of attention to my jasmine specs.

Tonight that changes:


Ugh.

Well, the first error—the "ReferenceError: Cal is not defined" error—likely has much to do with the remaining errors. Prior to the require.js switch, I had been including the monolithic version of my Backbone application in my SpecRunner.html thusly:
  <!-- include source files here... -->
  <script type="text/javascript" src="../public/javascripts/calendar.js"></script>
Now that I have switched to require.js, my web page is including the Backbone application like so:
<script data-main="javascripts/main" src="javascripts/require.js"></script>
I cannot simply re-use that line in my Jasmine tests because the javascripts/main file (referenced by the data-main attribute) initializes the calendar application:
require.config({
  paths: {
    'jquery': 'jquery.min',
    'jquery-ui': 'jquery-ui.min'
  }
});

require(['calendar'], function(Calendar){
  var calendar = new Calendar($('#calendar'));
});
Most of my specs do that in a beforeEach() setup block. It will not do to already have instantiated my application.

Instead, I think I can leverage require.js loading to replace the standard on-page-load Jasmine initializer. For that, in my SpecRunner.html file, I start by loading in requirejs, then do a little configuration:
  <script type="text/javascript" src="../public/javascripts/require.js"></script>
  <script type="text/javascript">
require.config({
  baseUrl: '../public/javascripts',
  paths: {
    'jquery': 'jquery.min',
    'jquery-ui': 'jquery-ui.min'
  }
});

require(['calendar', 'backbone'], function(Calendar, Backbone){
  // ...
});
  </script>
The paths attribute to require.config comes directly from my application code—it tells require.js how to find jquery and jquery-ui (i.e. to use the minified versions of each). The baseUrl attribute is special to my spec setup. It tells require.js to look in the public/javascripts directory for the javascript files instead of the same directory as SpecRunner.html.

As for the actual running of the jasmine environment, I do a normal require.js require(). In there, I need to assign Backbone and Cal (my Backbone application) to global variables so that my specs can access them:
  <script type="text/javascript" src="../public/javascripts/require.js"></script>
  <script type="text/javascript">
require.config({ /* ... */ });

require(['calendar', 'backbone'], function(Calendar, Backbone){
  window.Cal = Calendar;
  window.Backbone = Backbone;

  var jasmineEnv = jasmine.getEnv();
  jasmineEnv.updateInterval = 1000;

  var trivialReporter = new jasmine.TrivialReporter();

  jasmineEnv.addReporter(trivialReporter);

  jasmineEnv.specFilter = function(spec) {
    return trivialReporter.specFilter(spec);
  };

  jasmineEnv.execute();
});
  </script>
The rest of that is just normal Jasmine initialization code¸although I do remove some onload trickery. The require.js machinations ensure that the code will not run except on page load.

And it works!


Well, almost. But 6 failing specs is much improved. I call it a night here and will fix the remaining three tomorrow.


Day #231

1 comment:

  1. Thank you for the post!
    In the "require(['calendar', 'backbone'], function(Calendar, Backbone){" the backbone and calendar are in your path?

    ReplyDelete