Testing Backbone.js Applications

by Paulo Ragonha | September 2013 | Open Source

This article by Paulo Ragonha, author of Jasmine JavaScript Testing, provides how you can write tests to a Backbone.js application. You will learn about what to test, and not to test, when using Backbone.js.

<p style="margin-left: 40px; margin-right: 40px;" align="center"><i>(For more resources related to this topic, see <a href="#more">here</a>.)</i></p> <p>Testing Backbone applications is no different than testing any other application; you are still going to drive your code from your specs, except that Backbone is already leveraging a lot of functionality for you, for free. So expect to write less code, and consequently less specs.</p> <h1>Testing Backbone Views</h1> <p>We already have seen some of the advantages of using the View pattern in Testing Frontend Code, and are already creating our interface components in such a manner. So how can a Backbone View be different from what we have done so far?</p> <p>It retains a lot of the patterns that we have discussed as best practices for creating maintainable browser code, but with some syntax sugar and automation to make our life easier.</p> <p>They are the glue code between the HTML and the model, and the Backbone View's main responsibility is to add behavior to the interface, while keeping it in sync with a model or collection.</p> <p>As we will see, Backbone's biggest triumph is how it makes an easy-to-handle DOM event delegation, a task usually done with jQuery.</p> <h2>Declaring a new View</h2> <p>Very similar to what we have seen so far, declaring a new View is going to be a matter of extending the base Backbone.Viewobject.</p> <p>To demonstrate how it works we need an example. We are going to create a new View and its responsibility is going to be to render a single investment on the screen.</p> <p>We are going to create it in such a way that allows its use by the InvestmentListView component.</p> <p>This is a new component and spec, written in src/InvestmentView.js and spec/InvestmentViewSpec.jsrespectively.</p> <p>In the spec file, we can write something similar to what we have seen previously:</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">describe("InvestmentView", function() {<br /> var view;<br /> beforeEach(function() {<br /> view = new InvestmentView();<br /> });<br /> it("should be a Backbone View", function() {<br /> expect(view).toEqual(jasmine.any(Backbone.View));<br /> });<br />});</p></pre> <p>Which translates into an implementation that extends the base Backbone View component:</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">(function (Backbone) {<br /> var InvestmentView = Backbone.View.extend()<br /> this.InvestmentView = InvestmentView;<br />})(Backbone);</p></pre> <p>And now we are ready to explore some of the new functionalities provided by Backbone.</p> <h2>The el property</h2> <p>Like the View pattern a Backbone View also has an attribute containing the reference to its DOM element.</p> <p>The difference here is that Backbone comes with it by default, providing:</p> <ul> <li>view.el: The DOM element</li> <li>view.$el: The jQuery object for that element</li> <li>view.$: A scoped jQuery lookup function (the same way we have implemented </li> </ul> <p>And if you don't provide an element on the constructor, it creates an element for you automatically. Of course the element it creates is not attached to the document, and is up to the View's user code to attach it.</p> <p>Here is a common pattern you see while using Views:</p> <ol> <li>Instantiate it: <pre><p style="white-space: pre;">var view = new InvestmentView();</p></pre> </li> <li>Call the render function to draw the View's components <pre><p style="white-space: pre;">view.render()</p></pre> </li> <li>Append its element to the page document: <pre><p style="white-space: pre;">$('body').append(view.el);</p></pre> </li> </ol> <p>Given our clean implementation of the InvestmentView, if you would go ahead and execute the preceding code on a clean page, you would get the following result:</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">&lt;body&gt;<br /> &lt;div&gt;&lt;/div&gt;<br />&lt;/body&gt; </p></pre> <p>An empty div element; that is the default element created by Backbone. But we can change that with a few configuration parameters on the InvestmentView declaration.</p> <p>Let's say we want the DOM element of InvestmentViewto be a list item (li) with an investment CSS class. We could write this spec using the familiar Jasmine jQuery matchers:</p> <p>&nbsp;</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">describe("InvestmentView", function() {<br /> var view;<br /> beforeEach(function() {<br /> view = new InvestmentView();<br /> });<br /> it("should be a list item with 'investment' class", function() {<br /> expect(view.$el).toBe('li.investment');<br /> });<br />});</p></pre> <p>You can see that we didn't use the setFixtures function, since we can run this test against the element instance available on the View.</p> <p>Now to the implementation; all we have to do, is define two simple attributes in the View definition, and Backbone will use them to create the View's element:</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">var InvestmentView = Backbone.View.extend({<br /> className: 'investment',<br /> tagName: 'li'<br />});</p></pre> <p>By looking at the implementation you might be wondering if we shouldn't test it like we did with in the Backbone Model: Sync and AJAX requests section. Here I would recommend against it, since you wouldn't get any benefit from that approach, as this spec is much more solid.</p> <p>That is great, but how do we add content to that DOM element? That is up to the render function we are going to see next.</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">var view = new InvestmentView({ el: $('body') });</p></pre> <p>But by letting the View handle its rendering, we get better componentization and we can also gain on performance.</p> <h2>Rendering</h2> <p>Now that we understand that it is a good idea to have an empty element available on the View, we must get into the details of how to draw on this empty canvas.</p> <p>Backbone Views already come with an available render function, but it is a dummy implementation, so it is up to you to define how it works.</p> <p>Going back to the InvestmentView example, let's add a new acceptance criterion to describe how it should be rendered. We are going to start by expecting that it renders the return of investment as a percentage value. Here is the spec implementation:</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">describe("InvestmentView", function() {<br /> var view, investment;<br /> beforeEach(function() {<br /> investment = new Investment();<br /> view = new InvestmentView({ model: investment });<br /> });<br /> describe("when rendering", function() {<br /> beforeEach(function() {<br /> investment.set('roi', 0.1);<br /> view.render();<br /> });<br /> it("should render the return of investment", function() {<br /> expect(view.$el).toContainHtml('10%');<br /> });<br /> });<br />});</p></pre> <p>That is a very standard spec with concepts that we have seen before and the implementation is just a matter of defining the render function on the InvestmentViewdeclaration:</p> <pre style="margin-left: 40px;"><p style="white-space: pre;">var InvestmentView = Backbone.View.extend({<br /> className: 'investment',<br /> tagName: 'li',<br /> render: function () {<br /> this.$el.html('&lt;p&gt;'+ formatedRoi.call(this) +'&lt;p&gt;');<br /> return this;<br /> }<br />});<br />function formatedRoi () {<br /> return (this.model.get('roi') * 100) + '%';<br />}</p></pre> <p>It is using the this.$el property to add some HTML content to the View's element. There are some details that are important for you to notice regarding the render function implementation:</p> <ul> <li>We are using the jQuery.html function, so that we can invoke the render function multiple times without duplicating the View's content.</li> <li>The render function returns the View instance once it has completed rendering. This is a common pattern to allow chained calls, such as: <pre><p style="white-space: pre;">$('body').append(new InvestmentView().render().el);</p></pre> </li> </ul> <p>Now back to the test. You can see that we weren't testing for the specific HTML snippet, but rather, that just 10 percent text was rendered. You could have done a more thoroughly written spec by checking the exact same HTML at the expectation, but that ends up adding test complexity with little benefit.</p> <h1>Summary</h1> <p>In this article, you have seen how to use Backbone to do some heavy lifting, allowing you to focus more on your application code. I showed you the power of events, and how they make integration between different components much easier, allowing you to keep your models and Views in sync.</p> <h2>Resources for Article:</h2> <hr size="1" noshade="noshade" /> <p><b><a name="more"><span style="color: #000000;">Further resources on this subject:</span></a></b></p> <ul> <li><a href="http://www.packtpub.com/article/the-architecture-of-javascriptmvc" target="_blank">The architecture of JavaScriptMVC</a> [Article]</li> <li><a href="http://www.packtpub.com/article/working-with-javascript-in-drupal6-part1" target="_blank">Working with JavaScript in Drupal 6: Part 1</a> [Article]</li> <li><a href="http://www.packtpub.com/article/syntax-validation-javascript-testing" target="_blank">Syntax Validation in JavaScript Testing</a> [Article]</li> </ul> <hr size="1" noshade="noshade" />

About the Author :


Books From Packt

<hr /> <table id="table42" style="border-collapse: collapse;" border="0" bordercolor="#cc6600" cellpadding="3" width="588"> <tbody> <tr> <td valign="top" width="68"> <p align="center"><span style="font-family: Verdana; font-size: xx-small;"> <a href="http://www.packtpub.com/drupal-6-javascript-and-jquery/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1847196160.png" border="0" alt="Drupal 6 JavaScript and jQuery: RAW" title="Drupal 6 JavaScript and jQuery: RAW" width="49" height="61" /></a></span><br /> <a href="http://www.packtpub.com/drupal-6-javascript-and-jquery/book/sl/typoext-abr/1108"> <span style="font-family: Verdana; color: #ff0000; font-size: xx-small;">Drupal 6 JavaScript and jQuery: RAW</span></a></p> </td> <td valign="top" width="68"> <p align="center"><span style="font-family: Verdana; font-size: xx-small;"> <a href="http://www.packtpub.com/wordpress-theme-design/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" class="style7" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1847193099.png" border="0" alt="WordPress Theme Design" title="WordPress Theme Design" width="50" height="62" /></a><br /> <span><span class="style1"> <a href="http://www.packtpub.com/wordpress-theme-design/book/sl/typoext-abr/1108"> <span style="color: #ff0000;">WordPress Theme Design</span></a></span></span></span></p> </td> <td valign="top" width="68"> <p align="center"><a href="http://www.packtpub.com/wordpress-for-business-bloggers/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1847195326.png" border="0" alt="WordPress for Business Bloggers" title="WordPress for Business Bloggers" width="49" height="61" /></a><span style="font-family: Verdana; font-size: xx-small;"><br /> <span> <span class="style1"><span style="color: #ff0000;"> <a href="http://www.packtpub.com/wordpress-for-business-bloggers/book/sl/typoext-abr/1108"> <span style="color: #ff0000;">WordPress for Business Bloggers</span></a></span></span></span></span></p> </td> <td valign="top" width="68"> <p align="center"><span style="font-family: Verdana; font-size: xx-small;"> <a href="http://www.packtpub.com/wordpress/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1904811892.png" border="0" alt="WordPress Complete" title="WordPress Complete" width="50" height="61" /></a><br /> </span> <a href="http://www.packtpub.com/wordpress/book/sl/typoext-abr/1108"> <span style="font-family: Verdana; color: #ff0000; font-size: xx-small;">WordPress Complete</span></a></p> </td> <td valign="top" width="68"> <p align="center"><a href="http://www.packtpub.com/drupal-6-create-powerful-websites/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" class="style7" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1847192971.png" border="0" alt="Building Powerful and Robust Websites with Drupal 6" title="Building Powerful and Robust Websites with Drupal 6" width="49" height="61" /></a><br /> <span style="font-family: Verdana; color: #ff0000;"> <span class="style9"> <a href="http://www.packtpub.com/drupal-6-create-powerful-websites/book/sl/typoext-abr/1108"> <span class="style1"><span style="color: #ff0000; font-size: xx-small;">Building Powerful and Robust Websites with Drupal 6</span></span></a></span></span></p> </td> <td valign="top" width="68"> <p align="center"><span style="font-family: Verdana; font-size: xx-small;"> <a href="http://www.packtpub.com/joomla-version-1-5/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" class="style7" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/184719530X.png" border="0" alt="Building Websites with Joomla! 1.5" title="Building Websites with Joomla! 1.5" width="50" height="62" /></a><br /> <span><span class="style8"> <a href="http://www.packtpub.com/joomla-version-1-5/book/sl/typoext-abr/1108"> <span class="style1"><span style="color: #ff0000;">Building Websites with Joomla! 1.5</span></span></a></span></span></span></p> </td> <td valign="top" width="68"> <p align="center"><a href="http://www.packtpub.com/drupal-6-themes/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" class="style7" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1847195660.png" border="0" alt="Drupal 6 Themes" title="Drupal 6 Themest" width="50" height="62" /></a><br /> <span style="font-family: Verdana; color: #ff0000;"> <span class="style9"> <a href="http://www.packtpub.com/drupal-6-themes/book/sl/typoext-abr/1108"> <span class="style1"><span style="color: #ff0000; font-size: xx-small;">Drupal 6 Themes</span></span></a></span></span></p> </td> <td valign="top" width="68"> <p align="center"><a href="http://www.packtpub.com/Professional-Plone-web-applications-CMS/book/sl/typoext-abr/1108"> <img style="border: 1px solid #000000;" class="style7" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://images.packtpub.com/images/50x61/1847191983.png" border="0" alt="Professional Plone Development" title="Professional Plone Development" width="50" height="62" /></a><br /> <span style="font-family: Verdana; color: #ff0000;"> <span class="style9"> <a href="http://www.packtpub.com/Professional-Plone-web-applications-CMS/book/sl/typoext-abr/1108"> <span class="style1"><span style="color: #ff0000; font-size: xx-small;">Professional Plone Development</span></span></a></span></span></p> </td> </tr> </tbody> </table> <hr />
No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Q
m
n
u
T
W
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software