Update 2/5/2012: replaced the jasmine-fixture description with examples using the current "affix()" API method.
My goal this morning is to explain why exactly I recommend against loading HTML fixtures from external files when writing unit tests.
At the heart of this issue: Jasmine's familiar RSpec-like syntax belies the newness of other aspects of writing user interface code for a browser. Most RSpec users come from Rails, where a "view" is just a static HTML artifact, as opposed to a dynamic, stateful component that's central to the user's experience. Writing clean specs for situations like asynchronous event callbacks and DOM interactions takes some thoughtfulness and practice.
And so it is with this fixture problem.
When people ask me this question they usually come from one of two perspectives:
How can my specs see markup that I load from a test-specific HTML fixture file?
The first question is a superset of the second, so I'll tackle it first: requiring a server runtime to run unit tests of your client-side code is not a good idea.
So once I've shot down the dream of processing server-side templates for use in Jasmine specs, the next question I hear is usually "well, how do I load flat HTML files in my specs?"
At the risk of demoralizing the hypothetical question-asker, I don't like doing this either. Here's why:
If a spec references an external HTML file, I can't read the spec code and understand it entirely without also reading the external HTML file.
Gigantic HTML files don't inflict much pain. As long as I'm writing HTML into a separate file, it won't pain me to shamelessly rip my entire page from Chrome's Web Inspector and paste it into a flat HTML file. But if I'm in the habit of defining my HTML fixtures inline with my spec code, I'm under a very healthy pressure to keep that code to a minimum, because after a few lines it's just noisy and distracting.
I define my HTML fixtures inline with the rest of my spec setup code. When I was first getting started with Jasmine, I used to do something like this:
$container = $('<div class="container"></div>').appendTo('body');
Yuck. That's pretty noisy. Nobody likes writing HTML inside of strings, and the afterEach is annoying to keep track of, as well.
So I wrote a little helper for myself called jasmine-fixture, and it allows a much lighter-weight definition of fixtures:
$container = affix('.container');
By default, jasmine-fixture's
affix method takes a string, which it will use to go and create elements on the DOM such that the very same string could be used as a jQuery selector. The goal is to be as compact as possible without requiring a significant context switch for the user. After the spec runs, it'll clean out everything that's been added to the DOM.
It allows slightly more complex interactions, as well:
$input = affix('.container form[id=myForm] input[value=42]')
The above will add elements that look something like this:
Obviously, the plugin allows for more terse fixture definition then pasting raw HTML into concatenated strings. What's more, it encourages simple contracts between markup and code. A class name is usually plenty to select on, and the fact that the spec isn't even specifying a
<div/> sends the message that the JS may even work with other block element types. (For more complex situations,
affix() can also be used as a jQuery plugin on existing jQuery objects, appending children beneath them.)