##Introduction One definition of unit testing is the process of taking the smallest piece of testable code in an application, isolating it from the remainder of your codebase and determining if it behaves exactly as expected. In this section, we'll be taking a look at how to unit test Backbone applications using a popular JavaScript testing framework called [Jasmine](http://pivotal.github.com/jasmine/) from Pivotal Labs. For an application to be considered 'well'-tested, distinct functionality should ideally have its own separate unit tests where it's tested against the different conditions you expect it to work under. All tests must pass before functionality is considered 'complete'. This allows developers to both modify a unit of code and it's dependencies with a level of confidence about whether these changes have caused any breakage. As a basic example of unit testing is where a developer may wish to assert whether passing specific values through to a sum function results in the correct output being returned. For an example more relevant to this book, we may wish to assert whether a user adding a new Todo item to a list correctly adds a Model of a specific type to a Todos Collection. When building modern web-applications, it's typically considered best-practice to include automated unit testing as a part of your development process. Whilst we'll be focusing on Jasmine as a solution for this, there are a number of other alternatives worth considering, including QUnit. ##Jasmine Jasmine describes itself as a behaviour-driven development (BDD) framework for testing JavaScript code. Before we jump into how the framework works, it's useful to understand exactly what [BDD](http://en.wikipedia.org/wiki/Behavior_Driven_Development) is. BDD is a second-generation testing approach first described by [Dan North](http://dannorth.net/introducing-bdd/) (the authority on BDD) which attempts to test the behaviour of software. It's considered second-generation as it came out of merging ideas from Domain driven design (DDD) and lean software development, helping teams to deliver high quality software by answering many of the more confusing questions early on in the agile process. Such questions commonly include those concerning documentation and testing. If you were to read a book on BDD, it's likely to also be described as being 'outside-in and pull-based'. The reason for this is that it borrows the idea of of pulling features from Lean manufacturing which effectively ensures that the right software solutions are being written by a) focusing on expected outputs of the system and b) ensuring these outputs are achieved. BDD recognizes that there are usually multiple stakeholders in a project and not a single amorphous user of the system. These different groups will be affected by the software being written in differing ways and will have a varying opinion of what quality in the system means to them. It's for this reason that it's important to understand who the software will be bringing value you and exactly what in it will be valuable to them. Finally, BDD relies on automation. Once you've defined the quality expected, your team will likely want to check on the functionality of the solution being built regularly and compare it to the results they expect. In order to facilitate this efficiently, the process has to be automated. BDD relies heavily on the automation of specification-testing and Jasmine is a tool which can assist with this. BDD helps both developers and non-technical stakeholders: * Better understand and represent the models of the problems being solved * Explain supported tests cases in a language that non-developers can read * Focus on minimizing translation of the technical code being written and the domain language spoken by the business What this means is that developers should be able to show Jasmine unit tests to a project stakeholder and (at a high level, thanks to a common vocabulary being used) they'll ideally be able to understand what the code supports. Developers often implement BDD in unison with another testing paradigm known as [TDD](http://en.wikipedia.org/wiki/Test-driven_development) (test-driven development). The main idea behind TDD is: * Write unit tests which describe the functionality you would like your code to support * Watch these tests fail (as the code to support them hasn't yet been written) * Write code to make the tests pass * Rinse, repeat and refactor In this chapter we're going to use both BDD (with TDD) to write unit tests for a Backbone application. ***Note:*** I've seen a lot of developers also opt for writing tests to validate behaviour of their code after having written it. While this is fine, note that it can come with pitfalls such as only testing for behaviour your code currently supports, rather than behaviour the problem needs to be supported. ##Suites, Specs & Spies When using Jasmine, you'll be writing suites and specifications (specs). Suites basically describe scenarios whilst specs describe what can be done in these scenarios. Each spec is a JavaScript function, described with a call to ```it()`` using a description string and a function. The description should describe the behaviour the particular unit of code should exhibit and keeping in mind BDD, it should ideally be meaningful. Here's an example of a basic spec: ```javascript it('should be incrementing in value', function(){ var counter = 0; counter++; }); ``` On it's own, a spec isn't particularly useful until expectations are set about the behaviour of the code. Expectations in specs are defined using the ```expect()``` function and an [expectation matcher](https://github.com/pivotal/jasmine/wiki/Matchers) (e.g toEqual(), toBeTruthy(), toContain()). A revised example using an expectation matcher would look like: ```javascript it('should be incrementing in value', function(){ var counter = 0; counter++; expect(counter).toEqual(1); }); ``` The above code passes our behavioural expectation as ```counter`` equals 1. Notice how easy this was to read the expectation on the last line (you probably grokked it without any explanation). Specs are grouped into suites which we describe using Jasmine's ```describe()``` function, again passing a string as a description and a function. The name/description for your suite is typically that of the component or module you're testing. Jasmine will use it as the group name when it reports the results of the specs you've asked it to run. A simple suite containing our sample spec could look like: ```javascript describe('Stats', function(){ it('can increment a number', function(){ ... }); it('can subtract a number', function(){ ... }); }); ``` Suites also share a functional scope and so it's possible to declare variables and functions inside a describe block which are accessible within specs: ```javascript describe('Stats', function(){ var counter = 1; it('can increment a number', function(){ // the counter was = 1 counter = counter + 1; expect(counter).toEqual(2); }); it('can subtract a number', function(){ // the counter was = 2 counter = counter + 3; expect(counter).toEqual(5); }); }); ``` ***Note:*** Suites are executed in the order in which their are described, which can be useful to know if you would prefer to see test results for specific parts of your application reported first. Jasmine also supports **spies** - a way to mock, spy and fake behaviour in our unit tests. Spies replace the function they're spying on, allowing us to simulate behaviour we would like to mock (i.e test free of the actual implementation). In the below example, we're spying on the ```setComplete``` method of a dummy Todo function to test that arguments can be passed to it as expected. ```javascript var Todo = function(){ }; Todo.prototype.setComplete = function (arg){ return arg; } describe('a simple spy', function(){ it('should spy on an instance method of a Todo', function(){ var myTodo = new Todo(); spyOn(myTodo, 'setComplete'); myTodo.setComplete('foo bar'); expect(myTodo.setComplete).toHaveBeenCalledWith('foo bar'); var myTodo2 = new Todo(); spyOn(myTodo2, 'setComplete'); expect(myTodo2.setComplete).not.toHaveBeenCalled(); }); }); ``` What you're more likely to use spies for is testing [asynchronous](http://en.wikipedia.org/wiki/Asynchronous_communication) behaviour in your application such as AJAX requests. Jasmine supports: * Writing tests which can mock AJAX requests using spies. This allows us to test code which runs before an AJAX request and right after. It's also possible to mock/fake responses the server can return and the benefit of this type of testing is that it's faster as no real calls are being made to a server * Asynchronous tests which don't rely on spies For the first kind of test, it's possible to both fake an AJAX request and verify that the request was both calling the correct URL and executed a callback where one was provided. ```javascript it("the callback should be executed on success", function () { spyOn($, "ajax").andCallFake(function(options) { options.success(); }); var callback = jasmine.createSpy(); getTodo(15, callback); expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/todos/15"); expect(callback).toHaveBeenCalled(); }); function getTodo(id, callback) { $.ajax({ type: "GET", url: "/todos/" + id, dataType: "json", success: callback }); } ``` If you feel lost having seen matchers like ```andCallFake()``` and ```toHaveBeenCalled()```, don't worry. All of these are Spy-specific matchers and are documented on the Jasmine [wiki](https://github.com/pivotal/jasmine/wiki/Spies). For the second type of test (asynchronous tests), we can take the above further by taking advantage of three other methods Jasmine supports: * runs(function) - a block which runs as if it was directly called * waits(timeout) - a native timeout before the next block is run * waitsFor(function, optional message, optional timeout) - a way to pause specs until some other work has completed. Jasmine waits until the supplied function returns true here before it moves on to the next block. ```javascript it("should make an actual AJAX request to a server", function () { var callback = jasmine.createSpy(); getTodo(16, callback); waitsFor(function() { return callback.callCount > 0; }); runs(function() { expect(callback).toHaveBeenCalled(); }); }); function getTodo(id, callback) { $.ajax({ type: "GET", url: "todos.json", dataType: "json", success: callback }); } ``` ***Note:*** It's useful to remember that when making real requests to a web server in your unit tests, this has the potential to massively slow down the speed at which tests run (due to many factors including server latency). As this also introduces an external dependency that can (and should) be minimised in your unit testing, it is strongly recommended that you opt for spies to remove the need for a web server to be used here. ##beforeEach and afterEach() Jasmine also supports specifying code that can be run before each (```beforeEach()```) and after each (```afterEach```) test. This is useful for enforcing consistent conditions (such as resetting variables that may be required by specs). In the following example, ```beforeEach()``` is used to create a new sample Todo model specs can use for testing attributes. ```javascript beforeEach(function(){ this.todo = new Backbone.Model({ text: "Buy some more groceries", done: false }); }); it("should contain a text value if not the default value", function(){ expect(this.todo.get('text')).toEqual("Buy some more groceries"); }); ``` Each nested ```describe()``` in your tests can have their own ```beforeEach()``` and ```afterEach()``` methods which support including setup and teardown methods relevant to a particular suite. We'll be using ```beforeEach()``` in practice a little later. ##Shared scope In the previous section you may have noticed that we initially declared a variable ```this.todo``` in our ```beforeEach()``` call and were then able to continue using this in ```afterEach()```. This is thanks to a powerful feature of Jasmine known as shared functional scope. Shared scope allows ```this``` properties to be common to all blocks (including ```runs()```), but not declared variables (i.e ```var```s). ##Getting setup Now that we've reviewed some fundamentals, let's go through downloading Jasmine and getting everything setup to write tests. A standalone release of Jasmine can be [downloaded](http://pivotal.github.com/jasmine/download.html) from the official release page. You'll need a file called SpecRunner.html in addition to the release. It can be downloaded from https://github.com/pivotal/jasmine/tree/master/lib/jasmine-core/example or as part of a download of the complete Jasmine [repo](https://github.com/pivotal/jasmine/zipball/master).Alternatively, you can ```git clone``` the main Jasmine repository from https://github.com/pivotal/jasmine.git. Let's review [SpecRunner.html](https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/SpecRunner.html): It first includes both Jasmine and the necessary CSS required for reporting: ``` ``` Next, some sample tests are included: ``` ``` And finally the sources being tested: ``` ``` ***Note:*** Below this section of SpecRunner is code responsible for running the actual tests. Given that we won't be covering modifying this code, I'm going to skip reviewing it. I do however encourage you to take a look through [PlayerSpec.js](https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/spec/PlayerSpec.js) and [SpecHelper.js](https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/spec/SpecHelper.js). They're a useful basic example to go through how a minimal set of tests might work. ##TDD With Backbone When developing applications with Backbone, it can be necessary to test both individual modules of code as well as modules, views, collections and routers. Taking a TDD approach to testing, let's review some specs for testing these Backbone components using the popular Backbone [Todo](https://github.com/addyosmani/todomvc/tree/master/todo-example/backbone) application. I would like to extend my thanks to Larry Myers for his [Koans](https://github.com/addyosmani/backbone-koans) project which both greatly helped here and will be used in the exercise portion of this chapter. ##Models The complexity of Backbone models can vary greatly depending on what your application is trying to achieve. In the following example, we're going to test default values, attributes, state changes and validation rules. First, we begin our suite for model testing using ```describe()```: ``` describe('Tests for Todo', function() { ``` Models should ideally have default values for attributes. This helps ensure that when creating instances without a value set for any specific attribute, a default one (e.g "") is used instead. The idea here is to allow your application to interact with models without any unexpected behaviour. In the following spec, we create a new Todo without any attributes passed then check to find out what the value of the ```text``` attribute is. As no value has been set, we expect a default value of ```""`` to be returned. ```javascript it('Can be created with default values for its attributes.', function() { var todo = new Todo(); expect(todo.get('text')).toBe(""); }); ``` If testing this spec before your models have been written, you'll incur a failing test, as expected. What's required for the spec to pass is a default value for the attribute ```text```. We can implement this default value with some other useful defaults (which we'll be using shortly) in our Todo model as follows: ```javascript window.Todo = Backbone.Model.extend({ defaults: function() { return { text: "", done: false, order: 0 }; } ``` Next, we want to test that our model will pass attributes that are set such that retrieving the value of these attributes after initialization will be what we expect. Notice that here, in addition to testing for an expected value for ```text```, we're also testing the other default values are what we expect them to be. ```javascript it('Will set passed attributes on the model instance when created.', function() { var todo = new Todo({ text: 'Get oil change for car.' }); // what are the values expected here for each of the // attributes in our Todo? expect(todo.get('text')).toBe("Get oil change for car."); expect(todo.get('done')).toBe(false); expect(todo.get('order')).toBe(0); }); ``` Backbone models support a model.change() event which is triggered when the state of a model changes. In the following example, by 'state' I'm referring to the value of a Todo model's attributes. The reason changes of state are important to test are that there may be state-dependant events in your application e.g you may wish to display a confirmation view once a Todo model has been updated. ```javascript it('Fires a custom event when the state changes.', function() { var spy = jasmine.createSpy('-change event callback-'); var todo = new Todo(); // how do we monitor changes of state? todo.bind('change', spy); // what would you need to do to force a change of state? todo.set({ text: 'Get oil change for car.' }); expect(spy).toHaveBeenCalled(); }); ``` It's common to include validation logic in your models to ensure both the input passed from users (and other modules) in the application are 'valid'. A Todo app may wish to validate the text input supplied in case it contains rude words. Similarly if we're storing the ```done``` state of a Todo item using booleans, we need to validate that truthy/falsy values are passed and not just any arbitrary string. In the following spec, we take advantage of the fact that validations which fail model.validate() trigger an "error" event. This allows us to test if validations are correctly failing when invalid input is supplied. We create an errorCallback spy using Jasmine's built in ```createSpy()``` method which allows us to spy on the error event as follows: ```javascript it('Can contain custom validation rules, and will trigger an error event on failed validation.', function() { var errorCallback = jasmine.createSpy('-error event callback-'); var todo = new Todo(); todo.bind('error', errorCallback); // What would you need to set on the todo properties to // cause validation to fail? todo.set({done:'a non-integer value'}); var errorArgs = errorCallback.mostRecentCall.args; expect(errorArgs).toBeDefined(); expect(errorArgs[0]).toBe(todo); expect(errorArgs[1]).toBe('Todo.done must be a boolean value.'); }); ```` The code to make the above failing test support validation is relatively simple. In our model, we override the validate() method (as recommended in the Backbone docs), checking to make sure a model both has a 'done' property and is a valid boolean before allowing it to pass. ```javascript validate: function(attrs) { if (attrs.hasOwnProperty('done') && !_.isBoolean(attrs.done)) { return 'Todo.done must be a boolean value.'; } } ``` If you would like to review the final code for our Todo model, you can find it below: ```javascript var NAUGHTY_WORDS = /crap|poop|hell|frogs/gi; function sanitize(str) { return str.replace(NAUGHTY_WORDS, 'rainbows'); } window.Todo = Backbone.Model.extend({ defaults: function() { return { text: '', done: false, order: 0 }; }, initialize: function() { this.set({text: sanitize(this.get('text'))}, {silent: true}); }, validate: function(attrs) { if (attrs.hasOwnProperty('done') && !_.isBoolean(attrs.done)) { return 'Todo.done must be a boolean value.'; } }, toggle: function() { this.save({done: !this.get("done")}); } }); ``` ##Collections We now need to define specs to tests a Backbone collection of Todo models (a TodoList). Collections are responsible for a number of list tasks including managing order and filtering. A few specific specs that come to mind when working with collections are: * Making sure we can add new Todo models as both objects and arrays * Attribute testing to make sure attributes such as the base URL of the collection are values we expect * Purposefully adding items with a status of ```done:true``` and checking against how many the collection thinks have been completed vs. those that are remaining In this section we're going to cover the first two of these with the third left as an extended exercise I recommend trying out. Testing Todo models can be added to a collection as objects or arrays is relatively trivial. First, we initialize a new TodoList collection and check to make sure it's length (i.e the number of Todo models it contains) is 0. Next, we add new Todos, both as objects and arrays, checking the length property of the collection at each stage to ensure the overall count is what we expect: ```javascript describe('Tests for TodoList', function() { it('Can add Model instances as objects and arrays.', function() { var todos = new TodoList(); expect(todos.length).toBe(0); todos.add({ text: 'Clean the kitchen' }); // how many todos have been added so far? expect(todos.length).toBe(1); todos.add([ { text: 'Do the laundry', done: true }, { text: 'Go to the gym'} ]); // how many are there in total now? expect(todos.length).toBe(3); }); ... ``` Similar to model attributes, it's also quite straight-forward to test attributes in collections. Here we have a spec that ensures the collection.url (i.e the url reference to the collection's location on the server) is what we expect it to be: ```javascript it('Can have a url property to define the basic url structure for all contained models.', function() { var todos = new TodoList(); // what has been specified as the url base in our model? expect(todos.url).toBe('/todos/'); }); ``` For the third spec, it's useful to remember that the implementation for our collection will have methods for filtering how many Todo items are done and how many are remaining - we can call these ```done()``` and ```remaining()```. Consider writing a spec which creates a new collection and adds one new model that has a preset ```done``` state of ```true``` and two others that have the default ```done``` state of ```false```. Testing the length of what's returned using ```done()``` and ```remaining()``` should allow us to know whether the state management in our application is working or needs a little tweaking. The final implementation for our TodoList collection can be found below: ```javascript window.TodoList = Backbone.Collection.extend({ model: Todo, url: '/todos/', done: function() { return this.filter(function(todo) { return todo.get('done'); }); }, remaining: function() { return this.without.apply(this, this.done()); }, nextOrder: function() { if (!this.length) { return 1; } return this.last().get('order') + 1; }, comparator: function(todo) { return todo.get('order'); } }); ``` ##Views Before we take a look at testing Backbone views, let's briefly review a jQuery plugin that can assist with writing Jasmine specs for them. **The Jasmine jQuery Plugin** As we know our Todo application will be using jQuery for DOM manipulation, there's a useful jQuery plugin called [jasmine-jquery](https://github.com/velesin/jasmine-jquery) we can use to help simplify BDD testing rendered elements that our views may produce. The plugin provides a number of additional Jasmine [matchers](https://github.com/pivotal/jasmine/wiki/Matchers) to help test jQuery wrapped sets such as: * ```toBe(jQuerySelector)``` e.g ```expect($('
')).toBe('div#some-id')``` * ```toBeChecked()``` e.g ```expect($('')).toBeChecked()``` * ```toBeSelected()``` e.g ```expect($('')).toBeSelected()``` and [many others](https://github.com/velesin/jasmine-jquery). The complete list of matchers supported can be found on the project homepage. It's useful to know that similar to the standard Jasmine matchers, the custom matchers above can be inverted using the .not prefix (i.e ```expect(x).not.toBe(y)```): ```expect($('
I am an example
')).not.toHaveText(/other/)``` jasmine-jquery also includes a fixtures model, allowing us to load in arbitrary HTML content we may wish to use in our tests. Fixtures can be used as follows: Include some HTML in an external fixtures file: some.fixture.html: ```
some HTML content
``` Next, inside our actual test we would load it as follows: ``` loadFixtures('some.fixture.html') $('some-fixture').myTestedPlugin(); expect($('#some-fixture')).to ``` The jasmine-jquery plugin is by default setup to load fixtures from a specific directory: spec/javascripts/fixtures. If you wish to configure this path you can do so by initially setting ```jasmine.getFixtures().fixturesPath = 'your custom path'```. Finally, jasmine-jquery includes support for spying on jQuery events without the need for any extra plumbing work. This can be done using the ```spyOnEvent()``` and ```assert(eventName).toHaveBeenTriggered(selector)``` functions. An example of usage may look as follows: ``` spyOnEvent($('#el'), 'click'); $('#el').click(); expect('click').toHaveBeenTriggeredOn($('#el')); ``` **View testing** In this section we will review three dimensions to writing specs for Backbone Views: initial setup, view rendering and finally templating. The latter two of these are the most commonly tested, however we'll review shortly why writing specs for the initialization of your views can also be of benefit. ##Initial setup At their most basic, specs for Backbone views should validate that they are being correctly tied to specific DOM elements and are backed by valid data models. The reason to consider doing this is that failures to such specs can trip up more complex tests later on and they're fairly simple to write, given the overall value offered. To help ensure a consistant testing setup for our specs, we use ```beforeEach()``` to append both an empty ```UL``` (#todoList) to the DOM and initialize a new instance of a TodoView using an empty Todo model. ```afterEach()``` is used to remove the previous #todoList ```UL``` as well as the previous instance of the view. ```javascript describe('Tests for TodoView', function() { beforeEach(function() { $('body').append(''); this.todoView = new TodoView({ model: new Todo() }); }); afterEach(function() { this.todoView.remove(); $('#todoList').remove(); }); ... ``` The first spec useful to write is a check that the TodoView we've created is using the correct ```tagName``` (element or className). The purpose of this test is to make sure it's been correctly tied to a DOM element when it was created. Backbone views typically create empty DOM elements once initialized, however these elements are not attached to the visible DOM in order to allow them to be constructed without an impact on the performance of rendering. ```javascript it('Should be tied to a DOM element when created, based off the property provided.', function() { //what html element tag name represents this view? expect(todoView.el.tagName.toLowerCase()).toBe('li'); }); ``` Once again, if the TodoView has not already been written, we will experience failing specs. Thankfully, solving this is as simple as creating a new Backbone.View with a specific ```tagName```. ```javascript var todoView = Backbone.View.extend({ tagName: "li" }); ``` If instead of testing against the ```tagName``` you would prefer to use a className instead, we can take advantage of jasmine-jquery's ```toHaveClass()``` matcher to cater for this. ```javascript it('Should have a class of "todos"'), function(){ expect($(this.view.el)).toHaveClass('todos'); }); ```` The ```toHaveClass()``` matcher operates on jQuery objects and if the plugin hadn't been used, an exception would have been incurred (it is of course also possible to test for the className by accessing el.className if not opting to use jasmine-jquery). You may have noticed that in ```beforeEach()```, we passed our view an initial (albeit unfilled) Todo model. Views should be backed by a model instance which provides data. As this is quite important to our view's ability to function, we can write a spec to ensure a model is both defined (using the ```toBeDefined()``` matcher) and then test attributes of the model to ensure defaults both exist and are the value we expect them to be. ```javascript it('Is backed by a model instance, which provides the data.', function() { expect(todoView.model).toBeDefined(); // what's the value for Todo.get('done') here? expect(todoView.model.get('done')).toBe(false); //or toBeFalsy() }); ``` ##View rendering Next we're going to take a look at writing specs for view rendering. Specifically, we want to test that our TodoView elements are actually rendering as expected. In smaller applications, those new to BDD might argue that visual confirmation of view rendering could replace unit testing of views. The reality is that when dealing with applications that might grow to multiple-views, it often makes sense to automate this process as much as possible from the get-go. There are also aspects of rendering that require verification beyond what is visually presented on-screen (which we'll see very shortly). We're going to begin testing views by writing two specs. The first spec will check that the view's ```render()``` method is correctly returning the view instance, which is necessary for chaining. Our second spec will check that the HTML produced is exactly what we expect based on the properties of the model instance that's been associated with our TodoView. Unlike some of the previous specs we've covered, this section will make greater use of ```beforeEach()``` to both demonstrate how to use nested suites and also ensure a consistant set of conditions for our specs. In our first view spec for TodoView, we're simply going to create a sample model (based on Todo) and instantiate a TodoView which associates it with the model. ```javascript describe("TodoView", function() { beforeEach(function() { this.model = new Backbone.Model({ text: "My Todo", order: 1, done: false }); this.view = new TodoView({model:this.model}); }); describe("Rendering", function() { it("returns the view object", function() { expect(this.view.render()).toEqual(this.view); }); it("produces the correct HTML", function() { this.view.render(); //let's use jasmine-jquery's toContain() to avoid //testing for the complete content of a todo's markup expect(this.view.el.innerHTML) .toContain(''); }); }); }); ``` Once these specs are run, only the second one ('produces the correct HTML') fails. Our first spec ('returns the view object'), which is testing that the TodoView instance is returned from ```render()```, only passed as this is Backbone's default behaviour. We haven't yet overwritten the ```render()``` method with our own version. **Note:** For the purposes of maintaining readability, all template examples in this section will use a minimal version of the following Todo view template. As it's relatively trivial to expand this, please feel free to refer to this sample if needed: ```
/>
<%= content %>
``` The second spec fails with the following message: Expected '' to contain ```''```. The reason for this is the default behaviour for render() doesn't create any markup. Let's write a replacement for render() which fixes this: ```javascript render: function() { var template = ''; var output = template .replace("<%= text %>", this.model.get('text')); $(this.el).html(output); return this; } ``` The above specifies an inline string template and replaces fields found in the template within the "<% %>" blocks with their corresponding values from the associated model. As we're now also returning the TodoView instance from the method, the first spec will also pass. It's worth noting that there are serious drawbacks to using HTML strings in your specs to test against like this. Even minor changes to your template (a simple tab or whitespace) would cause your spec to fail, despite the rendered output being the same. It's also more time consuming to maintain as most templates in real-world applications are significantly more complex. A better option for testing rendered output is using jQuery to both select and inspect values. With this in mind, let's re-write the specs, this time using some of the custom matchers offered by jasmine-jquery: ```javascript describe("Template", function() { beforeEach(function() { this.view.render(); }); it("has the correct text content", function() { expect($(this.view.el).find('todo-content')) .toHaveText('My Todo'); }); }); ``` It would be impossible to discuss unit testing without mentioning fixtures. Fixtures typically contain test data (e.g HTML) that is loaded in when needed (either locally or from an external file) for unit testing. So far we've been establishing jQuery expectations based on the view's el property. This works for a number of cases, however, there are instances where it may be necessary to render markup into the document. The most optimal way to handle this within specs is through using fixtures (another feature brought to us by the jasmine-jquery plugin). Re-writing the last spec to use fixtures would look as follows: ```javascript describe("TodoView", function() { beforeEach(function() { ... setFixtures(''); }); ... describe("Template", function() { beforeEach(function() { $('.todos').append(this.view.render().el); }); it("has the correct text content", function() { expect($('.todos').find('.todo-content')) .toHaveText('My Todo'); }); }); }); ``` What we're now doing in the above spec is appending the rendered todo item into the fixture. We then set expectations against the fixture, which may be something desirable when a view is setup against an element which already exists in the DOM. It would be necessary to provide both the fixture and test the ```el``` property correctly picking up the element expected when the view is instantiated. ##Rendering with a templating system JavaScript templating systems (such as Handlebars, Mustache and even Underscore's own Micro-templating) support conditional logic in template strings. What this effectively means is that we can add if/else/ternery expressions inline which can then be evaluated as needed, allowing us to build even more powerful templates. In our case, when a user sets a Todo item to be complete (done), we may wish to provide them with visual feedback (such as a striked line through the text) to differenciate the item from those that are remaining. This can be done by attaching a new class to the item. Let's begin by writing a test we would ideally like to work: ```javascript describe("When a todo is done", function() { beforeEach(function() { this.model.set({done: true}, {silent: true}); $('.todos').append(this.view.render().el); }); it("has a done class", function() { expect($('.todos .todo-content:first-child')) .toHaveClass("done"); }); }); ``` This will fail with the following message: Expected '' to have class 'done'. which can be fixed in the existing render() method as follows: ```javascript render: function() { var template = ''; var output = template .replace("<%= text %>", this.model.get('text')); $(this.el).html(output); if (this.model.get('done')) { this.$(".todo-content").addClass("done"); } return this; } ``` This can however get unweildly fairly quickly. As the logic in our templates increases, so does the complexity involved. This is where templates libraries can help. As mentioned earlier, there are a number of popular options available, but for the purposes of this chapter we're going to stick to using Underscore's built-in Microtemplating. Whilst there are more advanced options you're free to explore, the benefit of this is that no additional files are required and we can easily change the existing Jasmine specs without too much adjustment. The TodoView object modified to use Underscore templating would look as follows: ```javascript var TodoView = Backbone.View.extend({ tagName: "li", initialize: function(options) { this.template = _.template(options.template || ""); }, render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; }, ... }); ``` Above, the initialize() method compiles a supplied Underscore template (using the _.template() function) in the instantiation. A more common way of referencing templates is placing them in a script tag using a custom script type (e.g type="text/template"). As this isn't a script type any browser understands, it's simply ignored, however referencing the script by an id attribute allows the template to be kept separate to other parts of the page which wish to use it. In real world applications, it's preferable to either do this or load in templates stored in external files for testing. For testing purposes, we're going to continue using the string injection approach to keep things simple. There is however a useful trick that can be applied to automatically create or extend teamplates in the Jasmine scope for each test. By creating a new directory (say, 'templates') in the 'spec' folder and adding a new script file with the following contents, to jasmine.yml or SpecRunner.html, we can add a todo property which contains the Underscore template we wish to use: ```javascript beforeEach(function() { this.templates = _.extend(this.templates || {}, { todo: '' }); }); ``` To finish this off, we simply update our existing spec to reference the template when instantiating the TodoView object: ```javascript describe("TodoView", function() { beforeEach(function() { ... this.view = new TodoView({ model: this.model, template: this.templates.todo }); }); ... }); ``` The existing specs we've looked at would continue to pass using this approach, leaving us free to adjust the template with some additional conditional logic for Todos with a status of 'done': ```javascript beforeEach(function() { this.templates = _.extend(this.templates || {}, { todo: '' }); }); ``` This will now also pass without any issues. Remember that jasmine-jquery also supports loading external fixtures into your specs easily using it's build in ```loadFixtures()``` and ```readFixtures()``` methods. For more information, consider reading the official jasmine-jquery [docs](https://github.com/velesin/jasmine-jquery). ##Conclusions We have now covered how to write Jasmine tests for models, views and collections with Backbone.js. Whilst testing routing can at times be desirable, some developers feel it can be more optimal to leave this to third-party tools such as Selenium, so do keep this in mind. James Newbery was kind enough to help me with writing the Views section above and his articles on [Testing Backbone Apps With Sinon](http://tinnedfruit.com/2011/04/26/testing-backbone-apps-with-jasmine-sinon-3.html) were of great inspiration (you'll actually find some Handlebars examples of the view specs in part 3 of his article). If you would like to learn more about writing spies and mocks for Backbone using [SinonJS](http://sinonjs.org) as well as how to test Backbone routers, do consider reading his series. ##Exercise As an exercise, I recommend now downloading [Backbone Koans](https://github.com/addyosmani/backbone-koans) and trying to fix some of the purposefully failing tests it has to offer. This is an excellent way of not just learning how Jasmine specs and suites work, but working through the examples (without peaking back) will also put your Backbone skills to test too. **Note**: I've linked to this book's fork of Koans in case any project-specific changes are required, however, in case you would like to check out the original project on GitHub, please feel [free](https://github.com/larrymyers/backbone-koans) to. ##Further reading * [Jasmine + Backbone Revisited](http://japhr.blogspot.com/2011/11/jasmine-backbonejs-revisited.html) * [Backbone, PhantomJS and Jasmine](http://japhr.blogspot.com/2011/12/phantomjs-and-backbonejs-and-requirejs.html)