Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit Testing JavaScript and Backbone.JS

David Mosher
November 05, 2011

Unit Testing JavaScript and Backbone.JS

Why do we hate testing? How do we test JavaScript? How do we test Backbone.JS? (Credits to @searls for slides 6 through 12, used with permission).

David Mosher

November 05, 2011
Tweet

More Decks by David Mosher

Other Decks in Programming

Transcript

  1. UNIT testing js Saturday, 5 November, 11

  2. @dmosher Saturday, 5 November, 11

  3. testing sucks Saturday, 5 November, 11

  4. testing sucks Saturday, 5 November, 11

  5. testing sucks js Saturday, 5 November, 11

  6. testing sucks js php Saturday, 5 November, 11

  7. testing sucks js php ??? Saturday, 5 November, 11

  8. Saturday, 5 November, 11

  9. because Saturday, 5 November, 11

  10. because don’t want to Saturday, 5 November, 11

  11. because don’t want to jaded Saturday, 5 November, 11

  12. because don’t want to don’t know how jaded Saturday, 5

    November, 11
  13. because don’t want to afraid don’t know how jaded Saturday,

    5 November, 11
  14. because don’t want to afraid don’t know how jaded ?

    Saturday, 5 November, 11
  15. every developer shares a secret FEAR Saturday, 5 November, 11

  16. MY CODE SUCKS? what if Saturday, 5 November, 11

  17. AND WE COPE POORLY Saturday, 5 November, 11

  18. fear causes us to: Saturday, 5 November, 11

  19. fear causes us to: pair less Saturday, 5 November, 11

  20. fear causes us to: pair less share less Saturday, 5

    November, 11
  21. fear causes us to: pair less share less change less

    Saturday, 5 November, 11
  22. fear causes us to: Saturday, 5 November, 11

  23. fear causes us to: defer more Saturday, 5 November, 11

  24. fear causes us to: defer more defend more Saturday, 5

    November, 11
  25. fear causes us to: defer more defend more deflect more

    Saturday, 5 November, 11
  26. say it with me: Saturday, 5 November, 11

  27. say it with me: “my code sucks” Saturday, 5 November,

    11
  28. so fail Saturday, 5 November, 11

  29. so fail PROUDLY Saturday, 5 November, 11

  30. so fail Saturday, 5 November, 11

  31. so fail FASTER Saturday, 5 November, 11

  32. Saturday, 5 November, 11

  33. because Saturday, 5 November, 11

  34. because YOUR CODE CAN SUCK Saturday, 5 November, 11

  35. because YOUR CODE CAN SUCK IF YOU HAVE Saturday, 5

    November, 11
  36. because YOUR CODE CAN SUCK TESTS IF YOU HAVE Saturday,

    5 November, 11
  37. because YOUR CODE CAN SUCK TESTS IF YOU HAVE sort

    of ... Saturday, 5 November, 11
  38. YOUR CODE CAN SUCK TESTS IF YOU HAVE sort of

    ... Saturday, 5 November, 11
  39. TESTS IF YOU HAVE sort of ... Saturday, 5 November,

    11
  40. IF YOU HAVE sort of ... Saturday, 5 November, 11

  41. sort of ... Saturday, 5 November, 11

  42. with tests my code can suck Saturday, 5 November, 11

  43. with tests my code can suck safety net Saturday, 5

    November, 11
  44. with tests my code can suck safety net freedom to

    unsuck Saturday, 5 November, 11
  45. with tests my code can suck safety net freedom to

    unsuck change verified Saturday, 5 November, 11
  46. with tests my code can suck safety net freedom to

    unsuck change verified commit Saturday, 5 November, 11
  47. and that’s why Saturday, 5 November, 11

  48. testing rocks Saturday, 5 November, 11

  49. testing rocks Saturday, 5 November, 11

  50. testing rocks js Saturday, 5 November, 11

  51. Saturday, 5 November, 11

  52. if it not a unit test Saturday, 5 November, 11

  53. if it talks to the db not a unit test

    Saturday, 5 November, 11
  54. if it talks to the db talks across network not

    a unit test Saturday, 5 November, 11
  55. if it talks to the db talks across network won’t

    run out of order not a unit test Saturday, 5 November, 11
  56. if it talks to the db talks across network touches

    the filesystem won’t run out of order not a unit test Saturday, 5 November, 11
  57. unit test if it Saturday, 5 November, 11

  58. isolates boundaries unit test if it Saturday, 5 November, 11

  59. isolates boundaries mocks dependencies unit test if it Saturday, 5

    November, 11
  60. isolates boundaries mocks dependencies FFFFFFAAAAAAAAAST unit test if it Saturday,

    5 November, 11
  61. isolates boundaries mocks dependencies allows for fast feedback FFFFFFAAAAAAAAAST unit

    test if it Saturday, 5 November, 11
  62. RECAP Saturday, 5 November, 11

  63. Saturday, 5 November, 11

  64. embrace fear ... code sucks Saturday, 5 November, 11

  65. embrace fear ... code sucks tests === freedom Saturday, 5

    November, 11
  66. embrace fear ... code sucks tests === freedom if Saturday,

    5 November, 11
  67. embrace fear ... code sucks tests === freedom fast, isolating,

    units if Saturday, 5 November, 11
  68. now that we all love testing ... :) Saturday, 5

    November, 11
  69. SHIFT architectures Saturday, 5 November, 11

  70. security persisting data analytics application logic HTML templating routing users

    proxying UI events things servers are great at Saturday, 5 November, 11
  71. security persisting data analytics application logic HTML templating routing users

    proxying things servers are great at Saturday, 5 November, 11
  72. security persisting data analytics HTML templating routing users proxying things

    servers are great at Saturday, 5 November, 11
  73. security persisting data analytics routing users proxying things servers are

    great at Saturday, 5 November, 11
  74. security persisting data analytics proxying things servers are great at

    Saturday, 5 November, 11
  75. it’s simpler when the client handles Saturday, 5 November, 11

  76. UI events it’s simpler when the client handles Saturday, 5

    November, 11
  77. application logic UI events it’s simpler when the client handles

    Saturday, 5 November, 11
  78. application logic HTML templating UI events it’s simpler when the

    client handles Saturday, 5 November, 11
  79. application logic HTML templating routing users UI events it’s simpler

    when the client handles Saturday, 5 November, 11
  80. BDD for JavaScript Saturday, 5 November, 11

  81. Saturday, 5 November, 11

  82. don’t Saturday, 5 November, 11

  83. don’t test jQuery Saturday, 5 November, 11

  84. don’t hit your server test jQuery Saturday, 5 November, 11

  85. don’t hit your server write async specs test jQuery Saturday,

    5 November, 11
  86. don’t hit your server test native DOM apis write async

    specs test jQuery Saturday, 5 November, 11
  87. <a href="#" class="logout">Logout</a> $(‘.logout’).click(function() { alert('cats'); }); don’t test jQuery

    Saturday, 5 November, 11
  88. <a href="#" class="logout">Logout</a> $(‘.logout’).click(function() { alert('cats'); }); it(‘binds the click

    event’, function() { spyOn(window, ‘alert’); $(‘.logout’).trigger(‘click’); // yuck expect(window.alert).toHaveBeenCalledWith(‘cats’); }); don’t test jQuery Saturday, 5 November, 11
  89. <a href="#" class="logout">Logout</a> var logout = function() { alert('cats'); };

    $(‘.logout’).click(logout); don’t test jQuery Saturday, 5 November, 11
  90. <a href="#" class="logout">Logout</a> var logout = function() { alert('cats'); };

    $(‘.logout’).click(logout); it(‘says cats’, function() { spyOn(window, ‘alert’); logout(); // test the unit directly expect(window.alert).toHaveBeenCalledWith(‘cats’); }); don’t test jQuery Saturday, 5 November, 11
  91. don’t hit your server var getDogs = function() { $.post(‘/get/dog’,

    function(dog) { $(‘ul.dog’).append(‘<li class=”dog”>’ + dog + ‘</li>’); }); }; don’t write async specs Saturday, 5 November, 11
  92. don’t hit your server var getDogs = function() { $.post(‘/get/dog’,

    function(dog) { $(‘ul.dog’).append(‘<li class=”dog”>’ + dog + ‘</li>’); }); }; it(‘hits my server’, function() { runs(function() { getDogs(); // yuck }); waits(500); // yuck runs(function() { // yuck expect($(‘ul.dogs’)).toContain(‘li.dog’); }); }); don’t write async specs Saturday, 5 November, 11
  93. don’t hit your server don’t write async specs Saturday, 5

    November, 11
  94. var getDogs = function() { $.post(‘/get/dog’, renderDog); }; var renderDog

    = function(dog) { $(‘ul.dog’).append(‘<li class=”dog”>’ + dog + ‘</li>’); } don’t hit your server don’t write async specs Saturday, 5 November, 11
  95. var getDogs = function() { $.post(‘/get/dog’, renderDog); }; var renderDog

    = function(dog) { $(‘ul.dog’).append(‘<li class=”dog”>’ + dog + ‘</li>’); } it(‘fetches a dog’, function() { // separation of concerns spyOn($, ‘post’); getDogs(); expect($.post).toHaveBeenCalledWith(‘/get/dog’, renderDogs); }); don’t hit your server don’t write async specs Saturday, 5 November, 11
  96. var getDogs = function() { $.post(‘/get/dog’, renderDog); }; var renderDog

    = function(dog) { $(‘ul.dog’).append(‘<li class=”dog”>’ + dog + ‘</li>’); } it(‘fetches a dog’, function() { // separation of concerns spyOn($, ‘post’); getDogs(); expect($.post).toHaveBeenCalledWith(‘/get/dog’, renderDogs); }); it(‘renders a dog’, function() { // isolated units renderDog(‘charlie’); expect($(ul.dog).find(‘.dog’)).toHaveText(‘charlie’); }); don’t hit your server don’t write async specs Saturday, 5 November, 11
  97. don’t test native DOM apis var loginView = function() {

    window.location.pathname = ‘/login’; }; Saturday, 5 November, 11
  98. don’t test native DOM apis var loginView = function() {

    window.location.pathname = ‘/login’; }; it(‘messes up my test runner’, function() { loginView(); expect(window.location.pathname).toBe(‘/login’); }); Saturday, 5 November, 11
  99. don’t test native DOM apis var redirectTo = function(spot) {

    // create a wrapper window.location.pathname = spot; } var loginView = function() { redirectTo(‘/login’); // use the wrapper in our unit }; Saturday, 5 November, 11
  100. don’t test native DOM apis var redirectTo = function(spot) {

    // create a wrapper window.location.pathname = spot; } var loginView = function() { redirectTo(‘/login’); // use the wrapper in our unit }; it(‘redirects to /login’, function() { spyOn(window, ‘redirectTo’); // mock our dependency loginView(); expect(redirectTo).toHaveBeenCalledWith(‘/login’); // win :) }); Saturday, 5 November, 11
  101. do Saturday, 5 November, 11

  102. do spyOn($, ‘ajax’); Saturday, 5 November, 11

  103. do spyOn($, ‘ajax’); mock html templates Saturday, 5 November, 11

  104. do spyOn($, ‘ajax’); mock html templates test the “plumbing” Saturday,

    5 November, 11
  105. do spyOn($, ‘ajax’); mock html templates write and test wrappers

    test the “plumbing” Saturday, 5 November, 11
  106. dospyOn($, ‘ajax’); var getDogs = function() { $.ajax({ // declarative

    and explicit arguments url: ‘/get/dog’, success: renderDog }); }; Saturday, 5 November, 11
  107. dospyOn($, ‘ajax’); var getDogs = function() { $.ajax({ // declarative

    and explicit arguments url: ‘/get/dog’, success: renderDog }); }; it(‘fetches a dog’, function() { // separation of concerns spyOn($, ‘ajax’); getDogs(); expect($.ajax).toHaveBeenCalledWith({ url: ‘/get/dog’, success: renderDogs }); }); Saturday, 5 November, 11
  108. <script type=”text/html” id=”user-tpl”> <li class=”user”>{{name}}</li> </script> <ul class=”users”></ul> var renderUser

    = function(name) { var t = _.template($(‘#user-tpl’).html()); // probably elsewhere $(‘.users’).append(t({name:name}); } do mock html templates dotest the “plumbing” Saturday, 5 November, 11
  109. <script type=”text/html” id=”user-tpl”> <li class=”user”>{{name}}</li> </script> <ul class=”users”></ul> var renderUser

    = function(name) { var t = _.template($(‘#user-tpl’).html()); // probably elsewhere $(‘.users’).append(t({name:name}); } it(‘verifies too much and depends on the real DOM’, function() { var $users = $(‘.users’); renderUser(‘nate’); expect($users).toContain(‘li.user’); // limits refactoring expect($users.find(‘.user’)).toHaveText(‘nate’); }); do mock html templates dotest the “plumbing” Saturday, 5 November, 11
  110. <script type=”text/html” id=”user-tpl”> <li class=”user”>{{name}}</li> </script> <ul class=”users”></ul> var renderUser

    = function(name) { var t = _.template($(‘#user-tpl’).html()); $(‘.users’).append(t({name:name}); } do mock html templates dotest the “plumbing” Saturday, 5 November, 11
  111. <script type=”text/html” id=”user-tpl”> <li class=”user”>{{name}}</li> </script> <ul class=”users”></ul> var renderUser

    = function(name) { var t = _.template($(‘#user-tpl’).html()); $(‘.users’).append(t({name:name}); } it(‘tests the plumbing’, function() { // tests only the keys/vals var $users = inject(‘ul.users’); // inject isolates btwn tests injectTemplate(‘#user-tpl’, [‘name’]); // emits {{name}} renderUser(‘nate’); expect($users).toHaveText(‘nate’); // refactor markup easier }); do mock html templates dotest the “plumbing” Saturday, 5 November, 11
  112. do write and test wrappers var redirectTo = function(spot) {

    // create a wrapper window.location.pathname = spot; } var loginView = function() { redirectTo(‘/login’); // use the wrapper in our unit }; Saturday, 5 November, 11
  113. do write and test wrappers var redirectTo = function(spot) {

    // create a wrapper window.location.pathname = spot; } var loginView = function() { redirectTo(‘/login’); // use the wrapper in our unit }; it(‘redirects to /login’, function() { spyOn(window, ‘redirectTo’); // mock our dependency loginView(); expect(window.redirectTo).toHaveBeenCalledWith(‘/login); // win :) }); Saturday, 5 November, 11
  114. limit Saturday, 5 November, 11

  115. limitcallback nesting Saturday, 5 November, 11

  116. limitcallback nesting anonymous functions Saturday, 5 November, 11

  117. limitcallback nesting DOM verification anonymous functions Saturday, 5 November, 11

  118. limit callback nesting Saturday, 5 November, 11

  119. limit callback nesting $.post(‘/level/1’, function(shallow) { $.post(‘/level/2’, function(deep) { doSomething(shallow);

    $.post(‘/level/3’, function(deeper) { doSomething(deep); $.post(‘/level/4’, function(deepest) { doSomething(deeper); doSomething(deepest); console.log(“INCEPTION”); }); }); }); }); // what is this i don’t even Saturday, 5 November, 11
  120. limit callback nesting Saturday, 5 November, 11

  121. limit callback nesting $.post(‘/level/1’, function(shallow) { APP.trigger(‘level-one-complete’, shallow); }); Saturday,

    5 November, 11
  122. limit callback nesting $.post(‘/level/1’, function(shallow) { APP.trigger(‘level-one-complete’, shallow); }); APP.bind(‘level-one-complete’,

    function(shallow) { doSomething(shallow); $.post(‘/level/2’, function(deep) { APP.trigger(‘level-two-complete’, deep); }); }); Saturday, 5 November, 11
  123. limit callback nesting $.post(‘/level/1’, function(shallow) { APP.trigger(‘level-one-complete’, shallow); }); APP.bind(‘level-one-complete’,

    function(shallow) { doSomething(shallow); $.post(‘/level/2’, function(deep) { APP.trigger(‘level-two-complete’, deep); }); }); etc... (but don’t use $.post!) Saturday, 5 November, 11
  124. limit anonymous functions Saturday, 5 November, 11

  125. limit anonymous functions $.ajax({ url: ‘/identity/crisis’, success: function(r) { //

    one (function(whoami) { // two alert(‘whoknows: ‘ + whoami); })(r); }, error: function(r) { // three (function(lostinspace) { // four alert(‘unknown: ‘ + lostinspace); })(r); } }); // anonymous overload! Saturday, 5 November, 11
  126. limit anonymous functions Saturday, 5 November, 11

  127. limit anonymous functions $.ajax({ url: ‘/identity/clarified’, success: whoAmI, error: lostInSpace

    }); // readability++ Saturday, 5 November, 11
  128. limit anonymous functions $.ajax({ url: ‘/identity/clarified’, success: whoAmI, error: lostInSpace

    }); // readability++ var whoAmI = function(identity) { alert(‘identified!: ‘ + identity); }; // testability++ var lostInSpace = function(ohnoes) { alert(‘lost!: ‘ + ohnoes); }; Saturday, 5 November, 11
  129. limit DOM verification Saturday, 5 November, 11

  130. limit DOM verification <div id=”happy”> <div id=”and”> <div id=”knows”></div> <div

    id=”it”></div> </div> </div> Saturday, 5 November, 11
  131. limit DOM verification <div id=”happy”> <div id=”and”> <div id=”knows”></div> <div

    id=”it”></div> </div> </div> describe(‘unnecessary verification’, function() { var $happy = $(“#happy”); expect($happy).toContain(“#and”); expect($happy).toContain(“#knows”); expect($happy).toContain(“#it”); }); // entirely unnecessary Saturday, 5 November, 11
  132. limit DOM verification Saturday, 5 November, 11

  133. limit DOM verification <ul class=”celebs”> {{{a-list celebs}}} // triple curly

    = escape html </ul> Handlebars.registerHelper(‘a-list’, function(celebs) { var output; _.each(celebs, function(celeb) { output += ‘<li>’ + celeb.name + ‘</li>’; }); return output; }); // testable unit, acceptable Saturday, 5 November, 11
  134. supply structure to js heavy apps Saturday, 5 November, 11

  135. Saturday, 5 November, 11

  136. WHY DO I USE IT? Saturday, 5 November, 11

  137. because this is painful Saturday, 5 November, 11

  138. $('#something') .append('<a href="javascript:void(0);">Linky</a>') .append('<a href="javascript:void(0);">Linky</a>') .append('<button onclick="app.doStuff()">Button</button>'); because this is

    painful Saturday, 5 November, 11
  139. $('#something') .append('<a href="javascript:void(0);">Linky</a>') .append('<a href="javascript:void(0);">Linky</a>') .append('<button onclick="app.doStuff()">Button</button>'); because this is

    painful http://james.padolsey.com/javascript/jquery-code-smells/ Saturday, 5 November, 11
  140. WHAT DOES IT DO? Saturday, 5 November, 11

  141. Saturday, 5 November, 11

  142. MODELS Saturday, 5 November, 11

  143. MODELS APP.Models.User = Backbone.Model.extend({ defaults: { first: ‘Ralph’, last: ‘Wiggum’

    } }); var me = new APP.Models.User({ first: ‘Dave’, last: ‘Mosher’ }); Saturday, 5 November, 11
  144. Saturday, 5 November, 11

  145. VIEWS Saturday, 5 November, 11

  146. VIEWS APP.Views.User = Backbone.View.extend({ el: ‘.user’, initialize: function() { this.model.bind(‘change’,

    this.updateUI); }, updateUI: function(model) { this.$(‘.first’).text(model.get(‘first’)); this.$(‘.last’).text(model.get(‘last’)); } }); Saturday, 5 November, 11
  147. VIEWS APP.Views.User = Backbone.View.extend({ el: ‘.user’, initialize: function() { this.model.bind(‘change’,

    this.updateUI); }, updateUI: function(model) { this.$(‘.first’).text(model.get(‘first’)); this.$(‘.last’).text(model.get(‘last’)); } }); new APP.Views.User({ model: new APP.Models.User }); me.set({first:‘Homer’, last:‘Simpson’); Saturday, 5 November, 11
  148. VIEWS APP.Views.User = Backbone.View.extend({ el: ‘.user’, initialize: function() { this.model.bind(‘change’,

    this.updateUI); }, updateUI: function(model) { this.$(‘.first’).text(model.get(‘first’)); this.$(‘.last’).text(model.get(‘last’)); } }); new APP.Views.User({ model: new APP.Models.User }); me.set({first:‘Homer’, last:‘Simpson’); <div class=”user”> <span class=”first”>Ralph</span> <span class=”last”>Wiggum</span> </div> Saturday, 5 November, 11
  149. VIEWS APP.Views.User = Backbone.View.extend({ el: ‘.user’, initialize: function() { this.model.bind(‘change’,

    this.updateUI); }, updateUI: function(model) { this.$(‘.first’).text(model.get(‘first’)); this.$(‘.last’).text(model.get(‘last’)); } }); new APP.Views.User({ model: new APP.Models.User }); me.set({first:‘Homer’, last:‘Simpson’); Saturday, 5 November, 11
  150. VIEWS APP.Views.User = Backbone.View.extend({ el: ‘.user’, initialize: function() { this.model.bind(‘change’,

    this.updateUI); }, updateUI: function(model) { this.$(‘.first’).text(model.get(‘first’)); this.$(‘.last’).text(model.get(‘last’)); } }); new APP.Views.User({ model: new APP.Models.User }); me.set({first:‘Homer’, last:‘Simpson’); <div class=”user”> <span class=”first”>Homer</span> <span class=”last”>Simpson</span> </div> Saturday, 5 November, 11
  151. Saturday, 5 November, 11

  152. EVENT HANDLERS Saturday, 5 November, 11

  153. EVENT HANDLERS document.addEventListener Saturday, 5 November, 11

  154. EVENT HANDLERS document.addEventListener $.click Saturday, 5 November, 11

  155. EVENT HANDLERS document.addEventListener $.click $.delegate ($.on/$.off for jQuery 1.7) Saturday,

    5 November, 11
  156. EVENT HANDLERS document.addEventListener $.click $.delegate ($.on/$.off for jQuery 1.7) Backbone.View

    -> events: {} Saturday, 5 November, 11
  157. Saturday, 5 November, 11

  158. VIEW EVENT HANDLERS Saturday, 5 November, 11

  159. VIEW EVENT HANDLERS APP.myView = new (Backbone.View.extend({ el: ‘#header .auth-widget’,

    events: { ‘click .login’ : ‘login’, ‘click .logout’ : ‘logout’ } }))(); Saturday, 5 November, 11
  160. HOW DO I TEST IT? Saturday, 5 November, 11

  161. testing events Saturday, 5 November, 11

  162. testing events describe(‘myView.events’, function() { it(‘defines these events’, function() {

    var expected = { ‘click .login’ : ‘login’, ‘click .logout’ : ‘logout’ }; expect(APP.myView.events).toEqual(expected); }); }); Saturday, 5 November, 11
  163. view functions Saturday, 5 November, 11

  164. view functions <a class=‘login’ data-name=‘dave’>login</a> login: function(e) { var $node

    = $(e.currentTarget); var name = $node.data(‘name’); alert(‘hi, ’ + name); } Saturday, 5 November, 11
  165. testing view functions Saturday, 5 November, 11

  166. testing view functions describe(‘myView.login’, function() { it(‘says my name’, function()

    { spyOn(window, ‘alert’); var e = { currentTarget: $(‘<a data-name=”dave”></a>’); }; myView.login(e); expect(window.alert).toHaveBeenCalledWith(‘hi, dave’); }); }); Saturday, 5 November, 11
  167. testing view functions describe(‘myView.login’, function() { it(‘says my name’, function()

    { spyOn(window, ‘alert’); var e = { currentTarget: $(‘<a data-name=”dave”></a>’); }; myView.login(e); expect(window.alert).toHaveBeenCalledWith(‘hi, dave’); }); }); // arrange Saturday, 5 November, 11
  168. testing view functions describe(‘myView.login’, function() { it(‘says my name’, function()

    { spyOn(window, ‘alert’); var e = { currentTarget: $(‘<a data-name=”dave”></a>’); }; myView.login(e); expect(window.alert).toHaveBeenCalledWith(‘hi, dave’); }); }); // arrange // act Saturday, 5 November, 11
  169. testing view functions describe(‘myView.login’, function() { it(‘says my name’, function()

    { spyOn(window, ‘alert’); var e = { currentTarget: $(‘<a data-name=”dave”></a>’); }; myView.login(e); expect(window.alert).toHaveBeenCalledWith(‘hi, dave’); }); }); // arrange // act // assert Saturday, 5 November, 11
  170. routers Saturday, 5 November, 11

  171. routers APP.Router = new (Backbone.Router.extend({ routes: { ‘/logout/:user’ : ‘logout’

    }, logout: function(user) { alert(‘bye, ‘ + user); } }))(); Saturday, 5 November, 11
  172. testing routes Saturday, 5 November, 11

  173. testing routes describe(‘APP.Router.logout’, function() { it(‘says bye’, function() { //

    arrange spyOn(window, ‘alert’); var router = APP.Router; // act router.logout(‘dave’); // unit router.navigate(‘/logout/dave’, true); // integration // assert expect(window.alert).toHaveBeenCalledWith(‘bye, dave’); }); }); Saturday, 5 November, 11
  174. models Saturday, 5 November, 11

  175. models APP.User = new (Backbone.Model.extend({ defaults: { name: ‘default’ },

    initialize: function() { this.bind(‘change:name’, this.render); }, render: function(model, name) { alert(name); } }))(); Saturday, 5 November, 11
  176. testing models Saturday, 5 November, 11

  177. testing models describe(‘APP.User.render’, function() { it(‘says my name’, function() {

    // arrange spyOn(window, ‘alert’); var user = APP.User; // act user.render(jasmine.any(Object), ‘ralph’); // unit user.set({ name: ‘ralph’ }); // integration // assert expect(window.alert).toHaveBeenCalledWith(‘ralph’); }); }); Saturday, 5 November, 11
  178. RECAP backbone.js Saturday, 5 November, 11

  179. Saturday, 5 November, 11

  180. arrange, act, assert Saturday, 5 November, 11

  181. arrange, act, assert mock, and mock some more Saturday, 5

    November, 11
  182. arrange, act, assert mock, and mock some more avoid integration

    tests Saturday, 5 November, 11
  183. TOOLS YOU SHOULD USE Saturday, 5 November, 11

  184. jasmine-fixture beforeEach(function(){ $container = inject('add-item'); }); <div id=”add-item”></div> by @searls

    Saturday, 5 November, 11
  185. jasmine-stealth beforeEach(function() { someSpy = jasmine.createSpy(); someSpy.when("panda",1).thenReturn("sad"); }); it("stubs accurately",

    function() { expect(someSpy("panda", 1)).toBe("sad")); }); also by @searls Saturday, 5 November, 11
  186. jasmine-jquery var $node = inject(‘#some-id’) expect($node).toBe('div#some-id') by @velesin var $a

    = $(‘<a id=”foo”>barcamp!</a>’) expect($a).toHaveText('barcamp!') Saturday, 5 November, 11
  187. tryjasmine.com by ... @searls compiles js and coffeescript saves specs

    between sessions (localStorage) Saturday, 5 November, 11
  188. more examples? github.com/davemo/backbone-slideshow test driven backbone.js 26 specs jasmine-bdd Saturday,

    5 November, 11
  189. acknowledgements @searls - for lots of tdd mentoring pillar technology

    Saturday, 5 November, 11
  190. WHERE CAN I LEARN? Saturday, 5 November, 11

  191. Tues, Nov 15 6:30pm - 8:30pm Location TBD @saskjavascript Saturday,

    5 November, 11
  192. QUESTIONS? github.com/davemo @dmosher Saturday, 5 November, 11