In this part, we’ll take a brief look at Backbone, and then move on to an introduction to some of the features of Jasmine and Sinon.JS. Along the way we’ll look at why these tools are a such good fit for testing Backbone applications.
In the last few months, Backbone.js has received a fair bit of exposure, with a number of tutorials and one or two high profile implementations.
Backbone’s MVC structure lends itself very nicely to bottom-up unit testing. The separation of concerns into models, collections, views and routers means that the behaviour of each ‘class’ (unit) can be tested in isolation, eliminating many bugs up front, and making debugging far simpler.
About Jasmine BDD
BDD emphasises shared language amongst developers and stakeholders. The ethos of BDD, and the way that specs are written in RSpec and Jasmine encourage developers to focus on testing the external behaviour of their code, rather than its internal details, with specs that are couched in terms of this shared language. This encourages the developer to always consider the beneficial value of the feature being developed, and focus on delivering it.
If you’re interested in learning more about BDD, then take a look at Dan North’s original BDD article, or even at the RSpec Book which has chapters dedicated to the subject, and is more of a BDD bible than an RSpec how-to.
So, let’s dig in and take a look at what Jasmine specs look like. Here is a rather simplistic example of a spec for a Backbone model using Jasmine:
A spec is simply a description of the expected behaviour, the code that will result in that behaviour, and one or more expectations that test the behaviour.
Individual specs should be short and only test one aspect of behaviour. If you find yourself writing a number of different expectations or a spec becomes quite long, then consider breaking it out into other specs. Grouping your specs with suites and using shared set up and teardown functions can help with this.
Specs are grouped into Suites, which are defined using the
describe() function. For example, all the specs for the Episode model could be grouped into a suite as follows:
Suites can also be nested, which is great when you have a lot of specs, as you can organise them into sets of discrete chunks. I like to use a
describe block to wrap specs relating to a particular starting context. This better retains the conversational style of the specs. For example:
beforeEach() and afterEach()
As in traditional xUnit style testing frameworks, you can optionally specify code to run before and after each test. This is great for ensuring consistent conditions for each test, and for setting up variables and objects to be used in your specs.
The example below uses
beforeEach() to create a model instance that will be used in each spec.
You can provide a
beforeEach() and an
afterEach() method for each nested describe that you have in your specs, allowing you to have both general and specific setup and teardown methods tailored for each suite of specs. As you’ll see in the other parts of this article, this is very handy indeed for reducing repetition and controlling the exact conditions for each spec.
The spec runner
This structure results in specs that are pretty easy for other developers to read and interpret directly, largely because of the description for each spec and the format of the expectation matchers.
Jasmine also provides a simple spec runner, which is simply an HTML page with a script that will run all the specs you provide. The following shows the output from a suite of specs with a single spec failure:
We’ll be introducing some other helpful features of Jasmine in the other parts of this article as we need them, including creating fixtures, working with jQuery and creating your own custom expectation matchers. Now, onto Sinon.JS.
- Performance - real DOM manipulation, reliance on timed behaviour and network activity slows tests down
- Isolation - unit tests should focus on as small a piece of functionality as possible, and be de-coupled from unreliable or slow dependencies
The use of fake objects is a fundamental part of embracing test-driven and behaviour-driven development. They essentially allow code to be tested in isolation from its dependencies. Any APIs or modules that your code under test depends upon can be faked to respond in the way you need for your test. You can also inspect the faked methods to see exactly how they were called during the course of a test.
Sinon.JS allows you to provide fakes for almost anything. You can fake parts of your own application, specific behaviours within jQuery, the
Sinon.JS provides three types of fake object: spies, stubs and mocks.
Spies are functions that keep track of how and often they were called, and what values were returned. This is phenomenally useful in asynchronous and event-driven applications as you can send a spy function off to keep track of what’s going on inside your methods, even if those methods are anonymous or closed off from direct inspection.
Spies can be ‘anonymous’ or can spy on existing functions.
An anonymous spy is just an empty function with spying features that can be sent off to record how it was used. Like a real spy being sent behind enemy lines with a microphone attached to it’s chest, the method under test is none the wiser. Here is an example of a spy testing a simple Backbone custom event binding:
This will pass if the spy was called one or more times, no matter how it was called or what the arguments were. However, Sinon provides a number of methods that allow you to be as strict as you like about the number of invocations, and indeed what each invocation looked like, and what the spy returned.
Spying behaviour can also be attached to an existing method. Hilariously, I like to call these ‘moles’. This is useful to check that some piece of functionality is calling another part of the code as expected. For example, you may want to check that a model’s
save method makes the correct jQuery
Stubs and Mocks
Stubs and mocks in Sinon implement all the features of spies, but with some added features. Stubs allow you to replace the existing behaviour of a particular method with whatever you like. This is great for emulating exceptions and error scenarios from external dependencies so you can test that your code will respond appropriately. It also allows you to start development when other dependencies are not yet in place.
Mocks provide all this, but instead mock an entire API and set built-in expectations on how they will be utilised. Like spies they track how they have been used, and like stubs they respond in a pre-programmed manner according to the needs of the test. However, unlike a spy, the expectations for their behaviour is pre-programmed, and a single verification step at the end will fail if any of these individual expectations are not met.
We’ll explore stubs and mocks as they needed in the other parts of this article.
Fake Ajax and fake servers
Sinon is not limited to spying on and stubbing plain functions and methods. It also provides shortcuts for faking Ajax responses. This means you can test your code in complete isolation from your JSON data source, and don’t depend on a running a web application in order to run your spec suites. Furthermore, you can test that your application responds appropriately when it strays from the happy path, including invalid JSON and various HTTP response codes.
Here’s a simple example of a spec for a Backbone model’s
fetch method that uses a fake server to respond to Ajax requests:
This spec can be made to pass with this simple Backbone model:
There is more to Sinon that we haven’t covered here. In particular, fake timers are very useful for testing time-dependent functionality such as animations without slowing down your tests. Check out the full documentation.
In the bleeding-edge world of Backbone applications, complex asynchronous and interdependent behaviours can cause any developer a major headache. Backbone helps developers to structure their code into small, self-contained models, collections, views and routers. But this is really only half the battle. Without well-tested code there will be a greater number of undetected bugs, and those that are discovered will be harder to track down. Other team members may unintentionally break your code, or simply misunderstand its purpose.
In the second part of this article, we will move on to actually testing some Backbone models and over time we’ll build up a simple working application with a suite of specs to go with it.