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

Magic Tricks of Testing (AncientCityRuby)

Magic Tricks of Testing (AncientCityRuby)

Tests are supposed to save us money. How is it, then, that many times they become millstones around our necks, gradually morphing into fragile, breakable things that raise the cost of change?

We write too many tests and we test the wrong kinds of things. This talk strips away the veil and offers simple, practical guidelines for choosing what to test and how to test it. Finding the right testing balance isn’t magic, it’s a magic trick; come and learn the secret of writing stable tests that protect your application at the lowest possible cost.

Sandi Metz

April 05, 2013
Tweet

More Decks by Sandi Metz

Other Decks in Programming

Transcript

  1. @sandimetz Apr 2013
    The Magic Tricks of
    Testing
    Sandi Metz
    Friday, April 5, 13

    View full-size slide

  2. @sandimetz Apr 2013
    Why do
    I hate my tests?
    Friday, April 5, 13

    View full-size slide

  3. @sandimetz Apr 2013
    Why do
    I hate my tests?
    Friday, April 5, 13

    View full-size slide

  4. @sandimetz Apr 2013
    They’re slow.
    Friday, April 5, 13

    View full-size slide

  5. @sandimetz Apr 2013
    They break every time
    I change something.
    Friday, April 5, 13

    View full-size slide

  6. @sandimetz Apr 2013
    They cost more than
    they’re worth.
    Friday, April 5, 13

    View full-size slide

  7. @sandimetz Apr 2013
    They are misery
    incarnate.
    Friday, April 5, 13

    View full-size slide

  8. @sandimetz Apr 2013
    It doesn’t have to be
    this way.
    Friday, April 5, 13

    View full-size slide

  9. @sandimetz Apr 2013
    You just need to
    delete some tests.
    Friday, April 5, 13

    View full-size slide

  10. @sandimetz Apr 2013
    Unit Tests: Goals
    Friday, April 5, 13

    View full-size slide

  11. @sandimetz Apr 2013
    Thorough
    Unit Tests: Goals
    Friday, April 5, 13

    View full-size slide

  12. @sandimetz Apr 2013
    Thorough
    Stable
    Unit Tests: Goals
    Friday, April 5, 13

    View full-size slide

  13. @sandimetz Apr 2013
    Thorough
    Stable
    Fast
    Unit Tests: Goals
    Friday, April 5, 13

    View full-size slide

  14. @sandimetz Apr 2013
    Thorough
    Stable
    Fast
    Few
    Unit Tests: Goals
    Friday, April 5, 13

    View full-size slide

  15. @sandimetz Apr 2013
    What does your app
    feel like?
    Friday, April 5, 13

    View full-size slide

  16. @sandimetz Apr 2013
    Friday, April 5, 13

    View full-size slide

  17. @sandimetz Apr 2013
    Focus on messages.
    Friday, April 5, 13

    View full-size slide

  18. @sandimetz Apr 2013
    Objects have a simple
    understanding of
    messages.
    Friday, April 5, 13

    View full-size slide

  19. @sandimetz Apr 2013
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  20. @sandimetz Apr 2013
    Friday, April 5, 13

    View full-size slide

  21. @sandimetz Apr 2013
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  22. @sandimetz Apr 2013
    Object
    Under
    Test
    Received from
    others
    Friday, April 5, 13

    View full-size slide

  23. @sandimetz Apr 2013
    Object
    Under
    Test
    Received from
    others
    Friday, April 5, 13

    View full-size slide

  24. @sandimetz Apr 2013
    Object
    Under
    Test
    Incoming
    Friday, April 5, 13

    View full-size slide

  25. @sandimetz Apr 2013
    Sent to
    others
    Incoming
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  26. @sandimetz Apr 2013
    Sent to
    others
    Incoming
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  27. @sandimetz Apr 2013
    Incoming Outgoing
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  28. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  29. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  30. @sandimetz Apr 2013
    Incoming
    Sent to Self
    Outgoing
    Message Origin
    Friday, April 5, 13

    View full-size slide

  31. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  32. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  33. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  34. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Query Command
    Object
    Under
    Test
    Friday, April 5, 13

    View full-size slide

  35. @sandimetz Apr 2013
    Query
    Command
    Message Type
    Friday, April 5, 13

    View full-size slide

  36. @sandimetz Apr 2013
    Query: Returns something
    but changes nothing.
    Command: Returns nothing
    but changes something.
    Type
    Friday, April 5, 13

    View full-size slide

  37. @sandimetz Apr 2013
    Query: Returns something
    but changes nothing.
    Command: Returns nothing
    but changes something.
    Type
    Friday, April 5, 13

    View full-size slide

  38. @sandimetz Apr 2013
    Query: Returns something
    but changes nothing.
    Command: Returns nothing
    but changes something.
    Type
    Friday, April 5, 13

    View full-size slide

  39. @sandimetz Apr 2013
    We conflate
    commands and queries
    at our peril.
    Friday, April 5, 13

    View full-size slide

  40. @sandimetz Apr 2013
    But we do it all the time.
    Friday, April 5, 13

    View full-size slide

  41. @sandimetz Apr 2013
    3 message Origins
    X
    2 message Types
    Friday, April 5, 13

    View full-size slide

  42. Query Command
    Message Origin x Type
    Outgoing
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Sent to Self
    Friday, April 5, 13

    View full-size slide

  43. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Part 1
    Incoming
    Friday, April 5, 13

    View full-size slide

  44. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Incoming
    Query Messages
    Incoming
    Friday, April 5, 13

    View full-size slide

  45. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Incoming
    Wheel
    diameter
    Friday, April 5, 13

    View full-size slide

  46. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Wheel
       attr_reader  :rim,  :tire
       def  initialize(rim,  tire)
       #  ...
       end
       def  diameter
           rim  +  (tire  *  2)
       end
       #  ...
    Friday, April 5, 13

    View full-size slide

  47. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Wheel
       attr_reader  :rim,  :tire
       def  initialize(rim,  tire)
       #  ...
       end
       def  diameter
           rim  +  (tire  *  2)
       end
       #  ...
    Friday, April 5, 13

    View full-size slide

  48. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Wheel
       attr_reader  :rim,  :tire
       def  initialize(rim,  tire)
       #  ...
       end
       def  diameter
           rim  +  (tire  *  2)
       end
       #  ...
    Friday, April 5, 13

    View full-size slide

  49. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  WheelTest  <  MiniTest::Unit::TestCase
       def  test_calculates_diameter
           wheel  =  Wheel.new(26,  1.5)
           assert_in_delta(29,
                                           wheel.diameter,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  50. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  WheelTest  <  MiniTest::Unit::TestCase
       def  test_calculates_diameter
           wheel  =  Wheel.new(26,  1.5)
           assert_in_delta(29,
                                           wheel.diameter,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  51. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  WheelTest  <  MiniTest::Unit::TestCase
       def  test_calculates_diameter
           wheel  =  Wheel.new(26,  1.5)
           assert_in_delta(29,
                                           wheel.diameter,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  52. @sandimetz Apr 2013
    Rule
    Friday, April 5, 13

    View full-size slide

  53. @sandimetz Apr 2013
    Test incoming query messages
    Rule
    Friday, April 5, 13

    View full-size slide

  54. @sandimetz Apr 2013
    Test incoming query messages
    by making assertions
    about what they send back.
    Rule
    Friday, April 5, 13

    View full-size slide

  55. Query Command
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  56. Query Command
    Assert
    result
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  57. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Incoming
    Another
    Incoming
    Query Message
    Friday, April 5, 13

    View full-size slide

  58. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    gear_inches
    Incoming
    Friday, April 5, 13

    View full-size slide

  59. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  60. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  61. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  62. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  63. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Wheel
    Friday, April 5, 13

    View full-size slide

  64. @sandimetz Apr 2013
    Sight along the edges of
    the space capsule.
    Friday, April 5, 13

    View full-size slide

  65. @sandimetz Apr 2013
    Sight along the edges of
    the space capsule.
    Friday, April 5, 13

    View full-size slide

  66. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  67. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  68. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  69. @sandimetz Apr 2013
    Test the interface,
    Friday, April 5, 13

    View full-size slide

  70. @sandimetz Apr 2013
    Test the interface,
    not the implementation.
    Friday, April 5, 13

    View full-size slide

  71. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Incoming
    Command Messages
    Incoming
    Friday, April 5, 13

    View full-size slide

  72. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    set_cog
    Incoming
    Friday, April 5, 13

    View full-size slide

  73. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
     attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  74. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
     attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  75. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
     attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
           #  ...
       end
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  76. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           gear  =  Gear.new
           gear.set_cog(27)
           assert(27,  gear.cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  77. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           gear  =  Gear.new
           gear.set_cog(27)
           assert(27,  gear.cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  78. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           gear  =  Gear.new
           gear.set_cog(27)
           assert(27,  gear.cog)
       end
    end
    Send the message
    Friday, April 5, 13

    View full-size slide

  79. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           gear  =  Gear.new
           gear.set_cog(27)
           assert(27,  gear.cog)
       end
    end
    Assert the side effect
    Friday, April 5, 13

    View full-size slide

  80. @sandimetz Apr 2013
    Rule
    Friday, April 5, 13

    View full-size slide

  81. @sandimetz Apr 2013
    Test incoming command messages
    Rule
    Friday, April 5, 13

    View full-size slide

  82. @sandimetz Apr 2013
    Test incoming command messages
    by making assertions
    about direct public side effects.
    Rule
    Friday, April 5, 13

    View full-size slide

  83. Query Command
    Assert
    result
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  84. Query Command
    Assert
    result
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Assert
    direct public
    side effects
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  85. @sandimetz Apr 2013
    DRY It Out
    Friday, April 5, 13

    View full-size slide

  86. @sandimetz Apr 2013
    Receiver of incoming message
    has sole responsibility
    DRY It Out
    Friday, April 5, 13

    View full-size slide

  87. @sandimetz Apr 2013
    Receiver of incoming message
    has sole responsibility
    for asserting
    the result
    direct public side effects.
    DRY It Out
    Friday, April 5, 13

    View full-size slide

  88. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Part 2
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  89. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming to Self
    Incoming
    Messages Sent to Self
    Friday, April 5, 13

    View full-size slide

  90. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  91. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  92. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_ratio
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(4.7,
                                           gear.ratio,
                                           0.01)
       end
    Friday, April 5, 13

    View full-size slide

  93. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_ratio
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(4.7,
                                           gear.ratio,
                                           0.01)
       end
    Test the private method?
    Friday, April 5, 13

    View full-size slide

  94. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_ratio
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(4.7,
                                           gear.ratio,
                                           0.01)
       end
    Friday, April 5, 13

    View full-size slide

  95. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_ratio
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(4.7,
                                           gear.ratio,
                                           0.01)
       end
    Friday, April 5, 13

    View full-size slide

  96. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_ratio
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(4.7,
                                           gear.ratio,
                                           0.01)
       end
    Redundant:
    Other tests already prove Gear works.
    Friday, April 5, 13

    View full-size slide

  97. Friday, April 5, 13

    View full-size slide

  98. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  99. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  100. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  101. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
         #  ...
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Friday, April 5, 13

    View full-size slide

  102. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  103. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
           gear.expect(:ratio)
           gear.verify
       end
    Friday, April 5, 13

    View full-size slide

  104. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
           gear.expect(:ratio)
           gear.verify
       end
    Expect to send
    the private method?
    Friday, April 5, 13

    View full-size slide

  105. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
           gear.expect(:ratio)
           gear.verify
       end
    Over Speci ed:
    Adds no safety yet breaks every
    time the implementation changes.
    Friday, April 5, 13

    View full-size slide

  106. Friday, April 5, 13

    View full-size slide

  107. @sandimetz Apr 2013
    Rule
    Friday, April 5, 13

    View full-size slide

  108. @sandimetz Apr 2013
    Do not test private methods.
    Rule
    Friday, April 5, 13

    View full-size slide

  109. @sandimetz Apr 2013
    Do not test private methods.
    Do not make assertions about their result.
    Rule
    Friday, April 5, 13

    View full-size slide

  110. @sandimetz Apr 2013
    Do not test private methods.
    Do not make assertions about their result.
    Do not expect to send them.
    Rule
    Friday, April 5, 13

    View full-size slide

  111. @sandimetz Apr 2013
    Temporarily breaking this rule
    can save $$$
    during development.
    Caveat
    Friday, April 5, 13

    View full-size slide

  112. Query Command
    Assert
    result
    Assert
    direct public
    side-effects
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  113. Query Command
    Assert
    result
    Assert
    direct public
    side-effects
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Ignore
    Message
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  114. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Part 3
    Outgoing
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  115. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming Outgoing
    to Self
    Incoming
    Outgoing
    Query Messages
    Friday, April 5, 13

    View full-size slide

  116. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming Outgoing
    to Self
    Incoming
    Spoiler Alert
    Same rules as ‘Sent to Self’
    Friday, April 5, 13

    View full-size slide

  117. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    diameter
    Gear
    gear_inches
    Wheel
    Outgoing
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  118. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
             #  ...
       end
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Outgoing
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  119. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)
             #  ...
       end
       def  gear_inches
           ratio  *  wheel.diameter
       end
     
       private
       def  ratio
           chainring  /  cog.to_f
       end
         #  ...
    end
    Outgoing
    to Self
    Incoming
    Wheel
    Friday, April 5, 13

    View full-size slide

  120. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    Gear
    Outgoing
    Wheel
    diameter
    Incoming
    Query
    Outgoing
    to Self
    Incoming
    gear_inches
    Friday, April 5, 13

    View full-size slide

  121. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming Outgoing
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  122. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  123. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
     
           assert_in_delta(29,
                                           gear.wheel.diameter,
                                           0.01)
       end
    end
    Friday, April 5, 13

    View full-size slide

  124. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
     
           assert_in_delta(29,
                                           gear.wheel.diameter,
                                           0.01)
       end
    end
    Assert result of
    outgoing query?
    Friday, April 5, 13

    View full-size slide

  125. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
     
           assert_in_delta(29,
                                           gear.wheel.diameter,
                                           0.01)
       end
    end
    Redundant:
    This duplicates Wheel’s tests.
    Friday, April 5, 13

    View full-size slide

  126. Friday, April 5, 13

    View full-size slide

  127. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming Outgoing
    to Self
    Incoming
    Friday, April 5, 13

    View full-size slide

  128. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
     
           gear.wheel.expect(:diameter)
           gear.verify
       end
    end
    Friday, April 5, 13

    View full-size slide

  129. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
     
           gear.wheel.expect(:diameter)
           gear.verify
       end
    end
    Expect to send
    outgoing query?
    Friday, April 5, 13

    View full-size slide

  130. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_calculates_gear_inches
           gear  =    Gear.new(
                               chainring:  52,
                               cog:              11,
                               wheel:          Wheel.new(26,  1.5))
           assert_in_delta(137.1,
                                           gear.gear_inches,
                                           0.01)
     
           gear.wheel.expect(:diameter)
           gear.verify
       end
    end
    Over Speci ed:
    Adds no safety yet breaks every
    time the implementation changes.
    Friday, April 5, 13

    View full-size slide

  131. Friday, April 5, 13

    View full-size slide

  132. @sandimetz Apr 2013
    Rule
    Friday, April 5, 13

    View full-size slide

  133. @sandimetz Apr 2013
    Rule
    Do not test
    outgoing query messages.
    Friday, April 5, 13

    View full-size slide

  134. @sandimetz Apr 2013
    Rule
    Do not test
    outgoing query messages.
    Do not make assertions about their result.
    Friday, April 5, 13

    View full-size slide

  135. @sandimetz Apr 2013
    Rule
    Do not test
    outgoing query messages.
    Do not make assertions about their result.
    Do not expect to send them.
    Friday, April 5, 13

    View full-size slide

  136. @sandimetz Apr 2013
    If the message has
    no visible side-effects
    Friday, April 5, 13

    View full-size slide

  137. @sandimetz Apr 2013
    If the message has
    no visible side-effects
    the sender
    should not test it.
    Friday, April 5, 13

    View full-size slide

  138. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  139. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  140. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Outgoing
    Command Messages
    to Self
    Incoming Outgoing
    Friday, April 5, 13

    View full-size slide

  141. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    diameter
    gear_inches
    set_cog
    changed
    to Self
    Incoming Outgoing
    Wheel
    Gear
    Obs
    Friday, April 5, 13

    View full-size slide

  142. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)  
               #  ...
     
       end
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Change #set_cog to add observer.
    Friday, April 5, 13

    View full-size slide

  143. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel
       def  initialize(args)  
               #  ...
     
       end
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  144. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
       attr_reader  :chainring,  :cog,  :wheel,  :observer
       def  initialize(args)  
               #  ...
           @observer    =  args[:observer]
       end
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  145. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
         #  ...
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  146. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
         #  ...
       def  set_cog(new_cog)
           @cog  =  new_cog
       end
    end
    Friday, April 5, 13

    View full-size slide

  147. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
         #  ...
       def  set_cog(new_cog)
           @cog  =  new_cog
           changed
           @cog
       end
       def  changed
           observer.changed(chainring,  cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  148. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
         #  ...
       def  set_cog(new_cog)
           @cog  =  new_cog
           changed
           @cog
       end
       def  changed
           observer.changed(chainring,  cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  149. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    set_cog
    changed
    Obs
    to Self
    Incoming Outgoing
    Friday, April 5, 13

    View full-size slide

  150. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    set_cog
    changed
    Obs
    to Self
    Incoming Outgoing
    Side
    Effects
    Friday, April 5, 13

    View full-size slide

  151. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    changed
    Gear
    set_cog
    Obs
    Incoming
    Command
    to Self
    Incoming Outgoing
    Side
    Effects
    Friday, April 5, 13

    View full-size slide

  152. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    Outgoing
    set_cog
    Obs
    changed
    Incoming
    Command
    to Self
    Incoming Outgoing
    Side
    Effects
    Friday, April 5, 13

    View full-size slide

  153. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           gear  =  Gear.new
           gear.set_cog(27)
           assert(27,  gear.cog)
       end
    end
    Make existing #set_cog test run.
    Friday, April 5, 13

    View full-size slide

  154. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           gear  =  Gear.new
                                       
                                       
                                       
           @gear.set_cog(27)
           assert(27,  @gear.cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  155. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           @observer  =  Obs.new
           gear  =  Gear.new
                                       
                                       
                                       
           @gear.set_cog(27)
           assert(27,  @gear.cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  156. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @gear.set_cog(27)
           assert(27,  @gear.cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  157. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @gear.set_cog(27)
           assert(27,  @gear.cog)
       end
    end
    Runs real @observer#changed
    1st version
    Friday, April 5, 13

    View full-size slide

  158. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_set_cog
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.stub(:changed,  @gear.set_cog(27))  do
               assert(27,  @gear.cog)
           end
       end
    end
    Variant
    Runs fake @observer#changed
    Friday, April 5, 13

    View full-size slide

  159. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
           #  ...
       def  set_cog(new_cog)
           @cog  =  new_cog
           changed
           @cog
       end
       def  changed
           observer.changed(chainring,  cog)
       end
    end
    Friday, April 5, 13

    View full-size slide

  160. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  Gear
           #  ...
       def  set_cog(new_cog)
           @cog  =  new_cog
           changed
           @cog
       end
       def  changed
           observer.changed(chainring,  cog)
       end
    end
    The app will be correct only if
    this message gets sent.
    Friday, April 5, 13

    View full-size slide

  161. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    Wheel
    Incoming
    Query
    changed
    Side
    Effects
    Gear
    Incoming
    Command
    Outgoing
    Obs
    Outgoing
    diameter
    gear_inches
    set_cog
    Friday, April 5, 13

    View full-size slide

  162. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    Side
    Effects
    Gear Wheel
    Obs
    Friday, April 5, 13

    View full-size slide

  163. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    Side
    Effects
    Gear Wheel
    Obs
    Friday, April 5, 13

    View full-size slide

  164. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    Side
    Effects
    Gear Wheel
    Obs
    Friday, April 5, 13

    View full-size slide

  165. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming Outgoing
    Friday, April 5, 13

    View full-size slide

  166. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    Friday, April 5, 13

    View full-size slide

  167. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    Friday, April 5, 13

    View full-size slide

  168. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    Friday, April 5, 13

    View full-size slide

  169. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    Depends on the distant side effect.
    Friday, April 5, 13

    View full-size slide

  170. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    Is this Gear’s responsibility?
    Friday, April 5, 13

    View full-size slide

  171. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    Is this Gear’s responsibility?
    Friday, April 5, 13

    View full-size slide

  172. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_saves_changed_cog_in_db
           @observer  =  Obs.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
             @gear.set_cog(27)
             #  assert  something  about  the  state  of  the  db
       end
    end
    This is an integration test.
    Friday, April 5, 13

    View full-size slide

  173. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Friday, April 5, 13

    View full-size slide

  174. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Friday, April 5, 13

    View full-size slide

  175. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Friday, April 5, 13

    View full-size slide

  176. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Friday, April 5, 13

    View full-size slide

  177. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Friday, April 5, 13

    View full-size slide

  178. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Friday, April 5, 13

    View full-size slide

  179. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Depends on the interface.
    Friday, April 5, 13

    View full-size slide

  180. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    Gear is responsible for sending
    #changed to @observer.
    Friday, April 5, 13

    View full-size slide

  181. @sandimetz Apr 2013
    Rule
    Friday, April 5, 13

    View full-size slide

  182. @sandimetz Apr 2013
    Expect to send
    outgoing command messages.
    Rule
    Friday, April 5, 13

    View full-size slide

  183. @sandimetz Apr 2013
    Breaking this rule
    can save $$$ if side effects
    are both stable and cheap.
    Caveat
    Friday, April 5, 13

    View full-size slide

  184. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    Ignore
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  185. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    Ignore Expect
    to send
    The Unit Testing Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  186. @sandimetz Apr 2013
    But
    Friday, April 5, 13

    View full-size slide

  187. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    What happens if
    the Observer class
    stops implementing #changed?
    Friday, April 5, 13

    View full-size slide

  188. @sandimetz Apr 2013
    Stubs
    Friday, April 5, 13

    View full-size slide

  189. @sandimetz Apr 2013
    Stubs
    Stubs de ne context.
    Friday, April 5, 13

    View full-size slide

  190. @sandimetz Apr 2013
    Stubs
    Stubs de ne context.
    Stub as needed (but zillions is a smell).
    Friday, April 5, 13

    View full-size slide

  191. @sandimetz Apr 2013
    Stubs
    Stubs de ne context.
    Stub as needed (but zillions is a smell).
    Avoid stubbing in the object under test.
    Friday, April 5, 13

    View full-size slide

  192. @sandimetz Apr 2013
    Stubs
    Stubs de ne context.
    Stub as needed (but zillions is a smell).
    Avoid stubbing in the object under test.
    Do not make assertions about stubs!
    Friday, April 5, 13

    View full-size slide

  193. @sandimetz Apr 2013
    Mocks
    Friday, April 5, 13

    View full-size slide

  194. @sandimetz Apr 2013
    Mocks
    Mocks test behavior.
    Friday, April 5, 13

    View full-size slide

  195. @sandimetz Apr 2013
    Mocks
    Mocks test behavior.
    Set expectations on mocks to test
    outgoing command messages.
    Friday, April 5, 13

    View full-size slide

  196. @sandimetz Apr 2013
    Mocks
    Mocks test behavior.
    Set expectations on mocks to test
    outgoing command messages.
    One expectation per test (do your best).
    Friday, April 5, 13

    View full-size slide

  197. @sandimetz Apr 2013
    Mocks
    Mocks test behavior.
    Set expectations on mocks to test
    outgoing command messages.
    One expectation per test (do your best).
    Do not stub using a mock!
    Friday, April 5, 13

    View full-size slide

  198. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    class  GearTest  <  MiniTest::Unit::TestCase
       def  test_notifies_observers_when_cogs_change
           @observer  =  MiniTest::Mock.new
           @gear          =  Gear.new(
                                       chainring:  52,
                                       cog:              11,
                                       observer:    @observer)
           @observer.expect(:changed,  true,  [52,  27])
           @gear.set_cog(27)
           @observer.verify
       end
    So really, what happens if
    Observer
    stops implementing #changed?
    Friday, April 5, 13

    View full-size slide

  199. @sandimetz Apr 2013
    Pain
    Friday, April 5, 13

    View full-size slide

  200. @sandimetz Apr 2013
    API Drift
    Pain
    Friday, April 5, 13

    View full-size slide

  201. @sandimetz Apr 2013
    Rule
    Friday, April 5, 13

    View full-size slide

  202. @sandimetz Apr 2013
    Honor the contract.
    Rule
    Friday, April 5, 13

    View full-size slide

  203. @sandimetz Apr 2013
    Honor the contract.
    Ensure test doubles stay in sync with the API.
    Rule
    Friday, April 5, 13

    View full-size slide

  204. @sandimetz Apr 2013
    Automagically
     minitest  requires  that  stubbed  methods  exist
     https://github.com/benmoss/quacky
     https://github.com/xaviershay/rspec-­‐fire
     https://github.com/cfcosta/minitest-­‐firemock
    Friday, April 5, 13

    View full-size slide

  205. @sandimetz Apr 2013
    Summary
    Friday, April 5, 13

    View full-size slide

  206. @sandimetz Apr 2013
    Friday, April 5, 13

    View full-size slide

  207. @sandimetz Apr 2013
    Friday, April 5, 13

    View full-size slide

  208. @sandimetz Apr 2013
    Strive for
    Thorough, Stable
    Fast, Few
    Friday, April 5, 13

    View full-size slide

  209. @sandimetz Apr 2013
    Test.
    Friday, April 5, 13

    View full-size slide

  210. @sandimetz Apr 2013
    Test.
    Everything.
    Friday, April 5, 13

    View full-size slide

  211. @sandimetz Apr 2013
    Test
    .
    Everything.
    Once.
    Friday, April 5, 13

    View full-size slide

  212. @sandimetz Apr 2013
    Test the
    interface,
    Friday, April 5, 13

    View full-size slide

  213. @sandimetz Apr 2013
    Test the
    interface,
    not the
    implementation.
    Friday, April 5, 13

    View full-size slide

  214. @sandimetz Apr 2013
    Guard against
    API drift.
    Friday, April 5, 13

    View full-size slide

  215. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    Ignore Expect
    to send
    Be a Minimalist
    Incoming
    Type
    POV
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Friday, April 5, 13

    View full-size slide

  216. @sandimetz Apr 2013
    Use good judgement.
    Friday, April 5, 13

    View full-size slide

  217. @sandimetz Apr 2013
    Insist on simplicity.
    Friday, April 5, 13

    View full-size slide

  218. @sandimetz Apr 2013
    Practice the Tricks.
    Friday, April 5, 13

    View full-size slide

  219. @sandimetz Apr 2013
    And go home
    every day saying...
    Friday, April 5, 13

    View full-size slide

  220. @sandimetz Apr 2013
    I love my tests.
    Friday, April 5, 13

    View full-size slide

  221. Thanks
    Sandi Metz
    @sandimetz
    Friday, April 5, 13

    View full-size slide

  222. http://www. ickr.com/photos/phil_shirley/5452562957/ thicket
    http://www. ickr.com/photos/zebble/6080622/ one padlock
    http://www. ickr.com/photos/chrisinplymouth/5543281168/ two padlocks
    http://www. ickr.com/photos/donjohnson395/2915304926/ twins at beach
    http://www.med.navy.mil/sites/nmcp/Patients/pediatrics/PublishingImages/binocular_boy.gif
    http://upload.wikimedia.org/wikipedia/commons/e/e1/Ordinary_bicycle02.jpg ordinary bike
    Magic Hat (82805980) Copyright Mmaxer - Shutterstock.com
    Dogs in Hats (39263399) Copyright MLoiselle - Fotolia.com
    Garden (35521385) Copyright Beboy - Fotolia.com
    Space Walk - Nasa http://grin.hq.nasa.gov/copyright.html
    Photo Credits
    Friday, April 5, 13

    View full-size slide

  223. @sandimetz Apr 2013
    And
    one
    last
    time...
    http://poodr.info
    Friday, April 5, 13

    View full-size slide

  224. @sandimetz Apr 2013
    http://poodr.info
    Friday, April 5, 13

    View full-size slide

  225. @sandimetz Apr 2013
    Hot
    off
    the
    press
    http://poodr.info
    Friday, April 5, 13

    View full-size slide

  226. Questions?
    Friday, April 5, 13

    View full-size slide

  227. @sandimetz Apr 2013
    http://poodr.info
    Friday, April 5, 13

    View full-size slide