Test-Driving Backbone Views With JQuery Templates, The Jasmine Gem, and Jasmine-JQuery

In my current production rails app, I use Cucumber to test my backbone code as an integration test – to make sure that the entire system is playing nice. Over the weekend, I decided to dive a little deeper into my test-driven-fu with Backbone apps. I wanted to get into the flow of test-driven development for all of my backbone applications, not just the plugins and complex logic scenarios that I have been doing so far.

A Roadblock: jQuery Selectors And Templates

I like jQuery Templates. They make my life with backbone much easier. But, I quickly ran into a problem when I got to the point where I wanted to start testing my backbone views that use jQuery Templates. Here’s the problem in a nutshell:

NutShell = Backbone.View.extend({
  initialize: function(){

    // road block!
    this.template = $("#some-template-id");

  }
});

And a template definition, stuff into my page’s HTML, like this:

<script id="some-template-id" type="text/x-jquery-tmpl">
  <h1>some template {{name}}</h1>
  <div id="foo">
    and stuff here. {{description}}
  </div>
</script>

The problem is that line in the middle of the backbone view – the one that uses jQuery to load up the contents of my template. How am I supposed to get access to my template definition when it’s stuck inside of an HTML page that my jasmine tests don’t have loaded up?!

Jasmine-JQuery Fixtures To The Rescue!

After floundering around for hours, I finally started seeing some potential solutions for this in having an external template file to use, and loading that up with the Jasmine-jQuery plugin and it’s ‘fixtures’ feature. I’ve read about fixtures before, but had not found a real need for them in my tests… until now. As it turns out, fixtures are exactly what I need. They let me either specify some HTML directly in my tests, or load up an external fixture – an HTML file – and have it be available in my tests.

After loading the Jamsine-jQuery plugin into my Jasmine tests, I only needed to add one line of code to have my template available:

describe("a test with a fixture", function(){
  beforeEach(function(){

    // load my template for use in this spec!
    loadFixtures("some-template-id.erb");

  });
  it("...", function(){ /* ... */ });
});

Now in my tests, I’ll have the template that I need available. My backbone view will be able to select it from the DOM, render it with my model or collection, and manipulate it as needed.

Potentially Duplicated HTML

There’s one more problem that using a fixture introduced, though. The default load path for fixtures is the ‘/spec/javascripts/fixtures’ folder. This makes sense as a default. Since fixtures are for tests, then the fixtures load path should be in the spec folder somewhere. However, my template is currently stuck inside of the HTML of my page.

To have my template available as a fixture, I needed to copy my template from my HTML page into a file in the fixtures folder. This sounds like duplication to me – one copy in the real HTML page and one copy in the fixtures folder. This might be OK for something small, but it would be a maintenance nightmare for a large problem with a lot of templates.

The solution I found is a combination of a few things: external template files, adjusting the load path of the fixtures, and rendering my templates into my HTML page as partial views.

External Template Files

To start with, I decided to put my templates into external files. I read several blog posts and stack overflow where people talked about doing this, and I decided to go ahead and give it a try. I need to have my templates available to both my actual application and my specs. At first, I thought this was going to end up in duplication, again.

After some more thought, I decided to try using a symlink. I’m using Sintra with ERB templates as the back-end for this app, so I put my template into the `./views` folder with a file name of `some-template-id.erb`. Then, I went into the `/spec/javascripts/` folder and I created a symlink:

cd spec/javascripts/
ln -s ../../views/ ./fixtures

Now my folder structure at `/spec/javascripts/fixtures` would point to my views folder. This would let the Jasmine-jQuery fixtures load the templates and also allow me to use my templates in my HTML views.

Rendering Templates As Partial Views

The next thing I needed to do was use my templates in my HTML page. I found an article by Dave Ward that talks about using external template files and loading them as-need via an AJAX request. However, I didn’t like this idea. If I have one or two templates on a page, this isn’t a big deal. But if I have 10 or 15 templates on a page, this would become a performance issue pretty quickly – even with HTTP Caching, etc. The initial load of each template would be a hit that I don’t want my users to have to take. Then there’s the issue of making sure the web server caches and sets the timeout for the template correctly… more than I wanted to deal with.

The solution that I came across, in the end, was much easier: render the template as a partial view. Since I already have ERB templates available on the server side, it was easy to take my template file and turn it into an ERB template that the server can render as a partial. All I had to do was render the partial into my HTML view:

<html>
  <head>

    <%= erb :'some-template-id' %>

    ...
  </head>
  <body>
    ...
  </body>
</html>

This allowed me to have my jQuery template set up for use with my actual application, in addition to it being available to my Jasmine specs.

Scrapping Symlinks. Adjust The Fixture Load Path

After reading some additional documentation for Jasmine-jQuery fixtures, I saw that it’s possible to change the load path for the templates. In addition, I’m using the Jasmine ruby gem to run my jasmine specs. This gem uses Sinatra (which I was already using in my app) to host the page that runs all of the jasmine specs. The Sinatra configuration for the Jasmine specs runs on port 8888 and opens up the entire folder structure of the application as part of the publicly accessible content via HTTP requests. This means that my `./views` folder is accessible via `http://localhost:8888/views/`. I scrapped the symlink, then, in favor of setting the load path for my fixtures.

jasmine.getFixtures().fixturesPath = '/views/';

I added this line of code to a file in `./spec/javascripts/helpers/` to ensure that it is always loaded and set for my Jasmine specs. Once I did this, my fixtures were now being loaded from from the views folder, via http requests, and I could drop the symlink for the fixtures folder.

Applicable To More Than Just Sinatra

One of my goals in setting this up was to have a solution that was applicable to more than just my current needs. Of course some of the implementation details that I’ve shown here are specific to Sinatra and the way I currently have things set up. However, the general idea of using external jQuery template file and rendering them into your HTML page for your actual application is almost universal to modern web development frameworks. This could be done easily in Rails, ASP.NET MVC, Django, Zend and other web server frameworks that allow partial views.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, JQuery, Model-View-Controller, Ruby, Unit Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Anonymous

    http://lostechies.com/johnteague/2010/06/28/unit-testing-your-mvc-views-with-qunit/ if you ever need to do it in MVC.  It’s a bit old school now and should be easier with the newer view engines.

    • Anonymous

      Not sure loading fixtures would work when using rake jasmine and MVC: since rake jasmine uses WEBrick and ASP.NET MVC another server, you’ll get a cross-origin error when attempting to load the MVC fixture.
      I suppose there is a way around it though, need to have a look at it.

  • http://twitter.com/Encosia Dave Ward

    Re: template loading, you might be interested in this followup post to the one you linked, if you haven’t seen it: http://encosia.com/jquery-templates-composite-rendering-and-remote-loading/

    That way you minimize the number of HTTP requests necessary to pull down logically related groups of templates, but you also don’t have to burden the initial page load with templates that aren’t used immediately. Of course, for templates that *are* used immediately as the page loads, I agree that it does make sense to go ahead and embed those in the view.

  • Tim Oxley

    I tried this method of importing templates as fixtures, but didn’t really like it. Another avenue for template loading which I prefer, is to use Sprockets from Rails 3.1.

    Sprockets (https://github.com/sstephenson/sprockets) has specific handling for javascript templates. To use your template ‘partials’ in your tests just require the template and use the JST array:

    //= require templates/my_template
    $(“#content”).html(JST["templates/my_template"](someDatas));

    …which is also the exact same way you require and utilize templates in your production JS code. It also feels nice and clean.

    Also, I wanted to use Handlebars as my template language of choice and thus required a Handlebars engine to Rails 3.1: https://github.com/leshill/handlebars_assets

    • http://mutedsolutions.com Derick Bailey

      nice! my solution well in sinatra, but i can see how this would be better if you’re in rails 3.1. thanks for the info! i’ll definitely look into this when i get on to a rails 3.1 project :)

  • Jason

    You might want to update the reference to .call() as it is now .bind().

  • Anonymous

    if
    cd spec/javascripts/
    must
    ln -s ../../app/views/ ./fixtures

    • http://mutedsolutions.com Derick Bailey

      i’ve used symbolic links for this, as well. it works well in *nix based systems (osx, linux, etc). when sharing code with windows users, though, they have to set up the link with a tool that doesn’t come directly with windows. tradeoffs… but it’s definitely a good option