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

Unit Testing JavaScript and Backbone.JS

Ead076bf445f9b50e3c094300e4690e9?s=47 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).

Ead076bf445f9b50e3c094300e4690e9?s=128

David Mosher

November 05, 2011
Tweet

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