$30 off During Our Annual Pro Sale. View Details »

JavaScript Testing Tactics ⚡️ Lightning Edition

JavaScript Testing Tactics ⚡️ Lightning Edition

Justin Searls

May 09, 2014
Tweet

More Decks by Justin Searls

Other Decks in Programming

Transcript

  1. #sjsJustin
    # Javascript Testing Tactics

    View Slide

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

    View Slide

  3. My name is Justin Searls
    Please tweet me @searls &
    Say [email protected]

    View Slide

  4. #sjsJustin
    ## background

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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*

    View Slide

  14. #sjsJustin
    ## syntax

    View Slide

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

    View Slide

  16. #sjsJustin
    ## syntax
    ### What I don't do
    !
    * use Jasmine's (RSpec-like) DSL

    View Slide

  17. View Slide

  18. describe("Math", function(){
    });

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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(){
    });
    });
    });

    View Slide

  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);
    });
    });
    });

    View Slide

  27. #sjsJustin
    ## syntax
    ### What I don't do
    !
    * use Jasmine's (RSpec-like) DSL
    * write my specs with JavaScript

    View Slide

  28. View Slide

  29. #sjsJustin
    ## syntax
    ### What's the problem?
    !

    View Slide

  30. #sjsJustin
    ## syntax
    ### What's the problem?
    !
    * Jasmine DSL is not obvious

    View Slide

  31. View Slide

  32. describe('thing', function(){});

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. !
    ...
    expect(spec).toFinallyEnd();
    });
    });
    });
    });
    });
    });

    View Slide

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

    View Slide

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

    View Slide

  43. #sjsJustin
    ## syntax
    ### What I do
    !

    View Slide

  44. #sjsJustin
    ## syntax
    ### What I do
    !
    * write specs in CoffeeScript

    View Slide

  45. View Slide

  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);
    });
    });
    });

    View Slide

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

    View Slide

  48. CoffeeScript basics*

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. this.save();

    View Slide

  53. @save()

    View Slide

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

    View Slide

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

    View Slide

  56. #sjsJustin
    ## syntax
    ### What I do
    !
    * write specs in CoffeeScript
    * use the *-given DSL

    View Slide

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

    View Slide

  58. describe "Math", ->
    Given -> @subject = new Math()
    !
    describe "#add", ->
    When -> @result = @subject.add(4,5)
    Then -> @result == 9

    View Slide

  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);
    });
    });
    });

    View Slide

  60. describe "Math", ->
    Given -> @subject = new Math()
    !
    describe "#add", ->
    When -> @result = @subject.add(4,5)
    Then -> @result == 9

    View Slide

  61. #sjsJustin
    ## syntax
    ### What I do
    !
    * write specs in CoffeeScript
    * use the *-given DSL
    * jasmine-given ported rspec-given

    View Slide

  62. #sjsJustin

    View Slide

  63. #sjsJustin
    ## syntax
    ### What I do
    !
    * write specs in CoffeeScript
    * use the *-given DSL
    * jasmine-given ported rspec-given
    * mocha-given ported jasmine-given

    View Slide

  64. #sjsJustin
    ## test runner

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  74. #sjsJustin
    ## test runner
    ### What I do
    !

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  78. #sjsJustin

    View Slide

  79. #sjsJustin
    ## ajax & ui events

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  84. #sjsJustin
    ### What's the problem?
    !
    ## ajax & ui events

    View Slide

  85. #sjsJustin
    ### What's the problem?
    !
    * too integrated for unit tests
    ## ajax & ui events

    View Slide

  86. #sjsJustin

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  90. #sjsJustin
    ### What I do
    !
    ## ajax & ui events

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  96. #sjsJustin

    View Slide

  97. #sjsJustin
    ## asynchronous code

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  107. #sjsJustin
    ## asynchronous code
    ### What I do
    !

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  112. #sjsJustin

    View Slide

  113. #sjsJustin
    ## the dom

    View Slide

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

    View Slide

  115. #sjsJustin
    ## the dom
    ### What I don't do
    !
    * say "(╯°□°╯ ┻━━┻" and
    avoid testing DOM interactions

    View Slide

  116. #sjsJustin
    You can test anything
    with functions

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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_"

    View Slide

  128. #sjsJustin
    ## the dom
    ### What I do
    !
    !

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  132. #sjsJustin

    View Slide

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

    View Slide

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

    View Slide

  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.
    !
    >

    View Slide

  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.
    !
    >

    View Slide

  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.
    >

    View Slide

  138. #sjsJustin
    ## Lineman
    !
    !
    !
    !
    !
    !
    Get up and running in minutes!
    !
    * [Docs](http://linemanjs.com)
    * [Help](http://twitter.com/linemanjs)

    View Slide

  139. My name is Justin Searls
    Please tweet me @searls &
    Say [email protected]

    View Slide