JavaScript Testing Tactics ⚡️ Lightning Edition

JavaScript Testing Tactics ⚡️ Lightning Edition

E6c6e133e74c3b83f04d2861deaa1c20?s=128

Justin Searls

May 09, 2014
Tweet

Transcript

  1. #sjsJustin # Javascript Testing Tactics

  2. #sjsJustin # How my JavaScript Tests differ from the README.

  3. My name is Justin Searls Please tweet me @searls &

    Say hello@testdouble.com
  4. #sjsJustin ## background

  5. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️

  6. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~
  7. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~
  8. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~ * ~~frameworks vs. TDD~~
  9. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~ * ~~frameworks vs. TDD~~ * a handful of situational tactics
  10. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~ * ~~frameworks vs. TDD~~ * a handful of situational tactics * using Jasmine
  11. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~ * ~~frameworks vs. TDD~~ * a handful of situational tactics * using Jasmine * generally applicable
  12. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~ * ~~frameworks vs. TDD~~ * a handful of situational tactics * using Jasmine * generally applicable-ish
  13. #sjsJustin ## background * ⚡️ 20 minutes! ⚡️ * ~~purposes

    of each type of test~~ * ~~integration tests~~ * ~~frameworks vs. TDD~~ * a handful of situational tactics * using Jasmine * generally applicable-ish *ymmv*
  14. #sjsJustin ## syntax

  15. #sjsJustin ## syntax ### What I don't do !

  16. #sjsJustin ## syntax ### What I don't do ! *

    use Jasmine's (RSpec-like) DSL
  17. None
  18. describe("Math", function(){ });

  19. describe("Math", function(){ var subject, result; });

  20. describe("Math", function(){ var subject, result; beforeEach(function(){ }); });

  21. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); });
  22. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ }); });
  23. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ beforeEach(function(){ }); }); });
  24. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); }); });
  25. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ }); }); });
  26. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ expect(result).toEqual(9); }); }); });
  27. #sjsJustin ## syntax ### What I don't do ! *

    use Jasmine's (RSpec-like) DSL * write my specs with JavaScript
  28. None
  29. #sjsJustin ## syntax ### What's the problem? !

  30. #sjsJustin ## syntax ### What's the problem? ! * Jasmine

    DSL is not obvious
  31. None
  32. describe('thing', function(){});

  33. describe('thing', function(){}); beforeEach(function(){});

  34. describe('thing', function(){}); beforeEach(function(){}); afterEach(function(){});

  35. describe('thing', function(){}); beforeEach(function(){}); afterEach(function(){}); it('does stuff', function(){});

  36. describe('thing', function(){}); beforeEach(function(){}); afterEach(function(){}); it('does stuff', function(){}); expect(true).toBeTruthy();

  37. describe('thing', function(){}); beforeEach(function(){}); afterEach(function(){}); it('does stuff', function(){}); expect(true).toBeTruthy(); this.addMatchers({});

  38. describe('thing', function(){}); beforeEach(function(){}); afterEach(function(){}); it('does stuff', function(){}); expect(true).toBeTruthy(); this.addMatchers({}); jasmine.createSpy().andCallThrough();

  39. #sjsJustin ## syntax ### What's the problem? ! * Jasmine

    DSL is not obvious * test code is verbose, unwieldy
  40. ! ... expect(spec).toFinallyEnd(); }); }); }); }); }); });

  41. #sjsJustin ## syntax ### What's the problem? ! * Jasmine

    DSL is not obvious * test code is verbose, unwieldy * those crying mustaches
  42. #sjsJustin ## syntax ### What's the problem? ! * Jasmine

    DSL is not obvious * test code is verbose, unwieldy * those crying mustaches });
  43. #sjsJustin ## syntax ### What I do !

  44. #sjsJustin ## syntax ### What I do ! * write

    specs in CoffeeScript
  45. None
  46. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ expect(result).toEqual(9); }); }); });
  47. describe "Math", -> beforeEach -> @subject = new Math() !

    describe "#add", -> beforeEach -> @result = @subject.add(4,5) ! it "adds", -> expect(@result).toEqual(9)
  48. CoffeeScript basics*

  49. CoffeeScript basics* ! *Fear not, it's just JS.

  50. var add = function(a,b) { return a + b; };

  51. add = (a,b) -> a + b

  52. this.save();

  53. @save()

  54. var self = this; save(function(){ self.display("Yay!"); });

  55. ! save => @display("Yay!") !

  56. #sjsJustin ## syntax ### What I do ! * write

    specs in CoffeeScript * use the *-given DSL
  57. describe "Math", -> beforeEach -> @subject = new Math() !

    describe "#add", -> beforeEach -> @result = @subject.add(4,5) ! it "adds", -> expect(@result).toEqual(9)
  58. describe "Math", -> Given -> @subject = new Math() !

    describe "#add", -> When -> @result = @subject.add(4,5) Then -> @result == 9
  59. describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math();

    }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ expect(result).toEqual(9); }); }); });
  60. describe "Math", -> Given -> @subject = new Math() !

    describe "#add", -> When -> @result = @subject.add(4,5) Then -> @result == 9
  61. #sjsJustin ## syntax ### What I do ! * write

    specs in CoffeeScript * use the *-given DSL * jasmine-given ported rspec-given
  62. #sjsJustin

  63. #sjsJustin ## syntax ### What I do ! * write

    specs in CoffeeScript * use the *-given DSL * jasmine-given ported rspec-given * mocha-given ported jasmine-given
  64. #sjsJustin ## test runner

  65. #sjsJustin ## test runner ### What I don't do !

  66. #sjsJustin ## test runner ### What I don't do !

    * default plain HTML test runner
  67. #sjsJustin ## test runner ### What I don't do !

    * default plain HTML test runner * jasmine-maven-plugin
  68. #sjsJustin ## test runner ### What I don't do !

    * default plain HTML test runner * jasmine-maven-plugin * jasmine-rails
  69. #sjsJustin ## test runner ### What I don't do !

    * default plain HTML test runner * jasmine-maven-plugin * jasmine-rails * any server-side-dependent plugin
  70. #sjsJustin ## test runner ### What's the problem? !

  71. #sjsJustin ## test runner ### What's the problem? ! *

    feedback isn't fast enough
  72. #sjsJustin ## test runner ### What's the problem? ! *

    feedback isn't fast enough * build tools treat JS as 2nd-class
  73. #sjsJustin ## test runner ### What's the problem? ! *

    feedback isn't fast enough * build tools treat JS as 2nd-class * friction of server-side coupling
  74. #sjsJustin ## test runner ### What I do !

  75. #sjsJustin ## test runner ### What I do ! *

    use Testem as my runner
  76. #sjsJustin ## test runner ### What I do ! *

    use Testem as my runner * use Lineman to build my code
  77. #sjsJustin ## test runner ### What I do ! *

    use Testem as my runner * use Lineman to build my code * runs tests in < 300ms on each file change
  78. #sjsJustin

  79. #sjsJustin ## ajax & ui events

  80. #sjsJustin ### What I don't do ! ## ajax &

    ui events
  81. #sjsJustin ### What I don't do ! * start a

    fake server that can stub & verify XHRs ## ajax & ui events
  82. #sjsJustin ### What I don't do ! * start a

    fake server that can stub & verify XHRs * monkey-patch the browser's XHR facilities ## ajax & ui events
  83. #sjsJustin ### What I don't do ! * start a

    fake server that can stub & verify XHRs * monkey-patch the browser's XHR facilities * invoke code by triggering UI events ## ajax & ui events
  84. #sjsJustin ### What's the problem? ! ## ajax & ui

    events
  85. #sjsJustin ### What's the problem? ! * too integrated for

    unit tests ## ajax & ui events
  86. #sjsJustin

  87. #sjsJustin ### What's the problem? ! * too integrated for

    unit tests * test pain isn't actionable (e.g. “only mock what you own”) ## ajax & ui events
  88. #sjsJustin ### What's the problem? ! * too integrated for

    unit tests * test pain isn't actionable (e.g. “only mock what you own”) * raises concerns better handled by integrated tests ## ajax & ui events
  89. #sjsJustin ### What's the problem? ! * too integrated for

    unit tests * test pain isn't actionable (e.g. “only mock what you own”) * raises concerns better handled by integrated tests * no pressure to improve private APIs ## ajax & ui events
  90. #sjsJustin ### What I do ! ## ajax & ui

    events
  91. #sjsJustin ### What I do ! * wrap native/3rd party

    libs with objects I own ## ajax & ui events
  92. #sjsJustin ### What I do ! * wrap native/3rd party

    libs with objects I own * in unit tests, mock wrappers away ## ajax & ui events
  93. #sjsJustin ### What I do ! * wrap native/3rd party

    libs with objects I own * in unit tests, mock wrappers away * test pain? -> improve wrapper API ! ## ajax & ui events
  94. #sjsJustin ### What I do ! * wrap native/3rd party

    libs with objects I own * in unit tests, mock wrappers away * test pain? -> improve wrapper API * wrappers specify our dependence ! ## ajax & ui events
  95. #sjsJustin ### What I do ! * wrap native/3rd party

    libs with objects I own * in unit tests, mock wrappers away * test pain? -> improve wrapper API * wrappers specify our dependence * don’t (typically) test wrappers ## ajax & ui events
  96. #sjsJustin

  97. #sjsJustin ## asynchronous code

  98. #sjsJustin ## asynchronous code ### What I don't do !

  99. #sjsJustin ## asynchronous code ### What I don't do !

    * write async tests for async code
  100. #sjsJustin ## asynchronous code ### What I don't do !

    * write async tests for async code unit ^
  101. #sjsJustin ## asynchronous code ### What's the problem? !

  102. #sjsJustin ## asynchronous code ### What's the problem? ! *

    test yields execution control
  103. #sjsJustin ## asynchronous code ### What's the problem? ! *

    test yields execution control * lots of noise in test setup
  104. #sjsJustin ## asynchronous code ### What's the problem? ! *

    test yields execution control * lots of noise in test setup * speed/timeout concerns
  105. #sjsJustin ## asynchronous code ### What's the problem? ! *

    test yields execution control * lots of noise in test setup * speed/timeout concerns * hard to debug race conditions
  106. #sjsJustin ## asynchronous code ### What's the problem? ! *

    test yields execution control * lots of noise in test setup * speed/timeout concerns * hard to debug race conditions * many reasons for tests to fail
  107. #sjsJustin ## asynchronous code ### What I do !

  108. #sjsJustin ## asynchronous code ### What I do ! *

    only write async APIs when useful
  109. #sjsJustin ## asynchronous code ### What I do ! *

    only write async APIs when useful * extract callbacks out of app code and into decorators and mixins
  110. #sjsJustin ## asynchronous code ### What I do ! *

    only write async APIs when useful * extract callbacks out of app code and into decorators and mixins * consider promises over callbacks
  111. #sjsJustin ## asynchronous code ### What I do ! *

    only write async APIs when useful * extract callbacks out of app code and into decorators and mixins * consider promises over callbacks * use jasmine-stealth to capture & discretely test callback functions
  112. #sjsJustin

  113. #sjsJustin ## the dom

  114. #sjsJustin ## the dom ### What I don't do !

  115. #sjsJustin ## the dom ### What I don't do !

    * say "(╯°□°╯ ┻━<table/>━┻" and avoid testing DOM interactions
  116. #sjsJustin You can test anything with functions

  117. #sjsJustin ## the dom ### What I don't do !

    * say "(╯°□°╯ ┻━<table/>━┻" and avoid testing DOM interactions * use HTML fixture files
  118. #sjsJustin ## the dom ### What I don't do !

    * say "(╯°□°╯ ┻━<table/>━┻" and avoid testing DOM interactions * use HTML fixture files shared ^
  119. #sjsJustin ## the dom ### What's the problem? #### not

    testing DOM interactions
  120. #sjsJustin ## the dom ### What's the problem? #### not

    testing DOM interactions ! * most JavaScript _is_ DOMy UI code
  121. #sjsJustin ## the dom ### What's the problem? #### not

    testing DOM interactions ! * most JavaScript _is_ DOMy UI code * low coverage limits suite's value
  122. #sjsJustin ## the dom ### What's the problem? #### not

    testing DOM interactions ! * most JavaScript _is_ DOMy UI code * low coverage limits suite's value * you'll write *more* DOM-heavy code via path of least resistance
  123. #sjsJustin ## the dom ### What's the problem? #### not

    testing DOM interactions ! * most JavaScript _is_ DOMy UI code * low coverage limits suite's value * you'll write *more* DOM-heavy code via path of least resistance * hampers true outside-in TDD
  124. #sjsJustin ## the dom ### What's the problem? #### using

    HTML fixture files !
  125. #sjsJustin ## the dom ### What's the problem? #### using

    HTML fixture files ! * large input -> larger everything !
  126. #sjsJustin ## the dom ### What's the problem? #### using

    HTML fixture files ! * large input -> larger everything * tests should push for small units
  127. #sjsJustin ## the dom ### What's the problem? #### using

    HTML fixture files ! * large input -> larger everything * tests should push for small units * sharing fixtures leads to a "_Tragedy of the Commons_"
  128. #sjsJustin ## the dom ### What I do ! !

  129. #sjsJustin ## the dom ### What I do ! *

    treat the DOM like a 3rd-party dependency, minimizing exposure !
  130. #sjsJustin ## the dom ### What I do ! *

    treat the DOM like a 3rd-party dependency, minimizing exposure * create HTML fixtures inline with jasmine-fixture !
  131. #sjsJustin ## the dom ### What I do ! *

    treat the DOM like a 3rd-party dependency, minimizing exposure * create HTML fixtures inline with jasmine-fixture * arrive at single-purpose DOM-aware functions
  132. #sjsJustin

  133. #sjsJustin ## less tactically ! Know _why_ you’re testing. >

  134. #sjsJustin ## less tactically ! Know _why_ you’re testing. !

    Push through the pain before deciding what is worth testing. >
  135. #sjsJustin ## less tactically ! Know _why_ you’re testing. !

    Push through the pain before deciding what is worth testing. ! Easy-to-test code is easy-to-use. Most JavaScript is hard-to-test. ! >
  136. #sjsJustin ## less tactically ! Know _why_ you’re testing. !

    Push through the pain before deciding what is worth testing. ! Easy-to-test code is easy-to-use. Most JavaScript is hard-to-test. ! >
  137. #sjsJustin ## less tactically ! Know _why_ you’re testing. !

    Push through the pain before deciding what is worth testing. ! Easy-to-test code is easy-to-use. Most JavaScript is hard-to-test. ! There’s no Right Way™ in software, just thoughtful and thoughtless approaches. >
  138. #sjsJustin ## Lineman ! ! ! ! ! ! Get

    up and running in minutes! ! * [Docs](http://linemanjs.com) * [Help](http://twitter.com/linemanjs)
  139. My name is Justin Searls Please tweet me @searls &

    Say hello@testdouble.com