In this post I’ll run through some typical Sammy scenarios and show you how I’ve written the corresponding Jasmine tests. For reference, I’m using Jasmine 0.11.1 and Sammy 0.5.4.
Setting up Sammy
Let’s start with a fairly simple Sammy app with some common features.
At the top of the app, the
context variable is assigned, providing easy and unambiguous access to the
Sammy.Application instance itself, for use elsewhere. A GET route is declared, which simply triggers an event named
myEvent. The second argument to
trigger passes an object to the event:
Here is the event that was triggered in the GET route. Bound events accept a custom object as their second argument, which is used in this example to pass in some arbitrary values:
Here, a (very) rough example of a modular design pattern is established for any non-event driven parts of the app. I like to group functions by similarity of purpose.
An aside: Can I read all this shit easily?
I’ve started to ask myself this question frequently. If the answer is no, it’s time to abstract.
Particularly where jQuery is involved, I often find code that looks similar to this:
Testing with Jasmine
Here’s an initial attempt at testing the above Sammy app using Jasmine. The specs are within
describe blocks. These aren’t strictly necessary but nesting them helps to describe your tests in relation to the structure of your app. See how the nesting follows the namespacing of the
url.build function, for example:
Since the app involves DOM insertion, it’s clear that some kind of setup and teardown of temporary DOM elements is required. Using Jasmine’s
beforeEach method, the relevant elements are inserted into the DOM before each spec is run. Since each spec within this
describe block refers to the same event, the event trigger is here as well, to avoid repetition. The DOM elements are cleaned up using
Specs are enclosed in the
it blocks and assertions/expectations are made using
expect. The syntax, when read out loud, sounds pretty close to how I’d describe my tests. “It should etc.”; “Expect actual to match expression”. Jasmine has various matcher methods such as
Befriending the DOM
There’s something a little clunky about certain aspects of the above tests. In particular the DOM-related areas look a little hacky. It would be unfair to call this a fault of the testing framework – DOM manipulation isn’t even on the Jasmine agenda. So this is where velesin’s jasmine-jquery extensions step in.
jasmine-jquery allows for various improvements on the original tests:
Firstly, the manual setup and teardown is no longer required, thanks to
loadFixtures, which loads in the necessary DOM elements from an external file, inserts them into a wrapper div inside the document body, and cleans up automatically after each spec is run. Big win. People who are really smart might even try to load fixtures in from the same files as partials to avoid duplication (I have a feeling this could all come together beautifully with Sammy’s new Meld feature…). An alternative is to use
setFixtures, which accepts either an HTML string or a jQuery element as its only argument rather than loading from an external file.
jasmine-jquery also provides a set of custom matchers. Here, for example, there’s no need for the workaround of checking the innerHtml of an element against a regular expression, when
toHaveText does the trick.
The Jasmine documentation could really do with a spruce up – especially in the less immediately obvious areas like spying, mocking and stubbing. It’s possible to bundle through and get some pretty nifty async tests running (blog post on this topic pending) but clearer instructions would be a big help. I’m sure better docs are in the pipeline with version 1.0.0 proper due to be released soon.
Provided you structure your Sammy application in a sensible way, it’s easily testable with Jasmine. I’d like a way of testing the Sammy routes themselves, something akin to functional testing as apposed to unit testing. But I’m happy enough with relying on the Sammy internal tests and keeping the routes as simple trigger calls.