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

Magic Tricks of Testing (RailsConf)

Magic Tricks of Testing (RailsConf)

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 30, 2013
Tweet

More Decks by Sandi Metz

Other Decks in Programming

Transcript

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  4. @sandimetz Apr 2013
    They’re slow
    Saturday, April 27, 13

    View full-size slide

  5. @sandimetz Apr 2013
    They’re fragile
    Saturday, April 27, 13

    View full-size slide

  6. @sandimetz Apr 2013
    They’re expensive
    Saturday, April 27, 13

    View full-size slide

  7. @sandimetz Apr 2013
    They are misery
    Saturday, April 27, 13

    View full-size slide

  8. @sandimetz Apr 2013
    They are misery
    incarnate
    Saturday, April 27, 13

    View full-size slide

  9. @sandimetz Apr 2013
    It doesn’t have to be
    this way
    Saturday, April 27, 13

    View full-size slide

  10. @sandimetz Apr 2013
    Just delete some tests
    Saturday, April 27, 13

    View full-size slide

  11. @sandimetz Apr 2013
    Unit Tests: Goals
    Saturday, April 27, 13

    View full-size slide

  12. @sandimetz Apr 2013
    Thorough
    Unit Tests: Goals
    Saturday, April 27, 13

    View full-size slide

  13. @sandimetz Apr 2013
    Thorough
    Stable
    Unit Tests: Goals
    Saturday, April 27, 13

    View full-size slide

  14. @sandimetz Apr 2013
    Thorough
    Stable
    Fast
    Unit Tests: Goals
    Saturday, April 27, 13

    View full-size slide

  15. @sandimetz Apr 2013
    Thorough
    Stable
    Fast
    Few
    Unit Tests: Goals
    Saturday, April 27, 13

    View full-size slide

  16. @sandimetz Apr 2013
    How does your app
    feel?
    Saturday, April 27, 13

    View full-size slide

  17. @sandimetz Apr 2013
    Saturday, April 27, 13

    View full-size slide

  18. @sandimetz Apr 2013
    Focus on messages
    Saturday, April 27, 13

    View full-size slide

  19. @sandimetz Apr 2013
    Objects are
    simple-minded
    Saturday, April 27, 13

    View full-size slide

  20. @sandimetz Apr 2013
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

  21. @sandimetz Apr 2013
    Saturday, April 27, 13

    View full-size slide

  22. @sandimetz Apr 2013
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

  23. @sandimetz Apr 2013
    Object
    Under
    Test
    Received from
    others
    Saturday, April 27, 13

    View full-size slide

  24. @sandimetz Apr 2013
    Object
    Under
    Test
    Received from
    others
    Saturday, April 27, 13

    View full-size slide

  25. @sandimetz Apr 2013
    Object
    Under
    Test
    Incoming
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  27. @sandimetz Apr 2013
    Sent to
    others
    Incoming
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

  28. @sandimetz Apr 2013
    Incoming Outgoing
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  30. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

  31. @sandimetz Apr 2013
    Incoming
    Sent to Self
    Outgoing
    Message Origin
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  34. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

  35. @sandimetz Apr 2013
    Sent to self
    Incoming Outgoing
    Query Command
    Object
    Under
    Test
    Saturday, April 27, 13

    View full-size slide

  36. @sandimetz Apr 2013
    Query
    Command
    Message Type
    Saturday, April 27, 13

    View full-size slide

  37. @sandimetz Apr 2013
    Query: Return something/
    change nothing
    Command: Return nothing/
    change something
    Type
    Saturday, April 27, 13

    View full-size slide

  38. @sandimetz Apr 2013
    Query: Return something/
    change nothing
    Command: Return nothing/
    change something
    Type
    Saturday, April 27, 13

    View full-size slide

  39. @sandimetz Apr 2013
    Query: Return something/
    change nothing
    Command: Return nothing/
    change something
    Type
    Saturday, April 27, 13

    View full-size slide

  40. @sandimetz Apr 2013
    We conflate
    commands and queries
    at our peril
    Saturday, April 27, 13

    View full-size slide

  41. @sandimetz Apr 2013
    but
    we do it all the time
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  45. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Incoming
    Wheel
    diameter
    Saturday, April 27, 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
       #  ...
    Saturday, April 27, 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
       #  ...
    Saturday, April 27, 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
       #  ...
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

  52. @sandimetz Apr 2013
    Rule
    Saturday, April 27, 13

    View full-size slide

  53. @sandimetz Apr 2013
    Test incoming query messages
    Rule
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  55. Query Command
    The Unit Testing Minimalist
    Incoming
    Type
    @sandimetz Apr 2013
    Message
    Sent to Self
    Outgoing
    Origin
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  58. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    gear_inches
    Incoming
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  65. @sandimetz Apr 2013
    Sight along the edges of
    the space capsule
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

  69. @sandimetz Apr 2013
    Test the interface
    Saturday, April 27, 13

    View full-size slide

  70. @sandimetz Apr 2013
    Test the interface
    (not the implementation)
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  72. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Gear
    set_cog
    Incoming
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

  80. @sandimetz Apr 2013
    Rule
    Saturday, April 27, 13

    View full-size slide

  81. @sandimetz Apr 2013
    Test incoming command messages
    Rule
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  85. @sandimetz Apr 2013
    DRY It Out
    Saturday, April 27, 13

    View full-size slide

  86. @sandimetz Apr 2013
    Receiver of incoming message
    has sole responsibility
    DRY It Out
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  89. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Messages Sent to Self
    to Self
    Incoming
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

  91. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming
    Saturday, April 27, 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
    Saturday, April 27, 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?
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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:
    #gear_inches test is proof enough
    Saturday, April 27, 13

    View full-size slide

  97. Saturday, April 27, 13

    View full-size slide

  98. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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?
    Saturday, April 27, 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 with every change
    Saturday, April 27, 13

    View full-size slide

  106. Saturday, April 27, 13

    View full-size slide

  107. @sandimetz Apr 2013
    Rule
    Saturday, April 27, 13

    View full-size slide

  108. @sandimetz Apr 2013
    Do not test private methods
    Rule
    Saturday, April 27, 13

    View full-size slide

  109. @sandimetz Apr 2013
    Do not test private methods
    Do not make assertions about their result
    Rule
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

  111. @sandimetz Apr 2013
    Break rule if it saves $$$
    during development
    Caveat
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  118. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    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
    Saturday, April 27, 13

    View full-size slide

  119. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self
    Incoming Outgoing
    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
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  121. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming to Self
    Incoming Outgoing
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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?
    Saturday, April 27, 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:
    Duplicates Wheel’s tests
    Saturday, April 27, 13

    View full-size slide

  126. Saturday, April 27, 13

    View full-size slide

  127. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming to Self
    Incoming Outgoing
    Saturday, April 27, 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
    Saturday, April 27, 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?
    Saturday, April 27, 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 costs but not bene ts
    Saturday, April 27, 13

    View full-size slide

  131. Saturday, April 27, 13

    View full-size slide

  132. @sandimetz Apr 2013
    Rule
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

  134. @sandimetz Apr 2013
    Rule
    Do not test
    outgoing query messages
    Do not make assertions about their result
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

  136. @sandimetz Apr 2013
    If a message has
    no visible side-effects
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  141. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    diameter
    gear_inches
    set_cog
    changed
    Wheel
    Gear
    Obs
    to Self
    Incoming Outgoing
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 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
    Saturday, April 27, 13

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  153. @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
    Saturday, April 27, 13

    View full-size slide

  154. @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
    This message MUST get sent
    Saturday, April 27, 13

    View full-size slide

  155. @sandimetz GoGaRuCo 2012
    @sandimetz Apr 2013
    to Self Outgoing
    Incoming
    Anti-Pattern
    to Self
    Incoming Outgoing
    Saturday, April 27, 13

    View full-size slide

  156. @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
    Saturday, April 27, 13

    View full-size slide

  157. @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
    Saturday, April 27, 13

    View full-size slide

  158. @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
    Saturday, April 27, 13

    View full-size slide

  159. @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
    Saturday, April 27, 13

    View full-size slide

  160. @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
    Saturday, April 27, 13

    View full-size slide

  161. @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?
    Saturday, April 27, 13

    View full-size slide

  162. @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?
    Saturday, April 27, 13

    View full-size slide

  163. @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
    Saturday, April 27, 13

    View full-size slide

  164. @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
    Saturday, April 27, 13

    View full-size slide

  165. @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
    Saturday, April 27, 13

    View full-size slide

  166. @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
    Saturday, April 27, 13

    View full-size slide

  167. @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
    Saturday, April 27, 13

    View full-size slide

  168. @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
    Saturday, April 27, 13

    View full-size slide

  169. @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
    Saturday, April 27, 13

    View full-size slide

  170. @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
    Saturday, April 27, 13

    View full-size slide

  171. @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
    Saturday, April 27, 13

    View full-size slide

  172. @sandimetz Apr 2013
    Rule
    Saturday, April 27, 13

    View full-size slide

  173. @sandimetz Apr 2013
    Expect to send
    outgoing command messages
    Rule
    Saturday, April 27, 13

    View full-size slide

  174. @sandimetz Apr 2013
    Break rule if side effects
    are stable and cheap
    Caveat
    Saturday, April 27, 13

    View full-size slide

  175. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    Ignore
    The Unit Testing Minimalist
    Incoming
    Type
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Origin
    Saturday, April 27, 13

    View full-size slide

  176. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    Ignore Expect
    to send
    The Unit Testing Minimalist
    Incoming
    Type
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Origin
    Saturday, April 27, 13

    View full-size slide

  177. @sandimetz Apr 2013
    But
    Saturday, April 27, 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
    What happens if Observer
    stops implementing #changed?
    Saturday, April 27, 13

    View full-size slide

  179. @sandimetz Apr 2013
    Pain
    Saturday, April 27, 13

    View full-size slide

  180. @sandimetz Apr 2013
    API Drift
    Pain
    Saturday, April 27, 13

    View full-size slide

  181. @sandimetz Apr 2013
    Rule
    Saturday, April 27, 13

    View full-size slide

  182. @sandimetz Apr 2013
    Honor the contract
    Rule
    Saturday, April 27, 13

    View full-size slide

  183. @sandimetz Apr 2013
    Honor the contract
    Ensure test doubles stay in sync with the API
    Rule
    Saturday, April 27, 13

    View full-size slide

  184. @sandimetz Apr 2013
    Automagically
     minitest: requires that stubbed methods exist
     rspec: #should_receive(:msg).and_call_original
     https://github.com/psyho/bogus
     https://github.com/benmoss/quacky
     https://github.com/xaviershay/rspec-­‐fire
     https://github.com/cfcosta/minitest-­‐firemock
    Saturday, April 27, 13

    View full-size slide

  185. @sandimetz Apr 2013
    Summary
    Saturday, April 27, 13

    View full-size slide

  186. @sandimetz Apr 2013
    Be a
    minimalist
    Saturday, April 27, 13

    View full-size slide

  187. Query Command
    Assert
    result
    Assert
    direct public
    side effects
    Ignore Expect
    to send
    Incoming
    Type
    @sandimetz Apr 2013
    Message
    Ignore
    Sent to Self
    Outgoing
    Origin
    Saturday, April 27, 13

    View full-size slide

  188. @sandimetz Apr 2013
    Use good
    judgement
    Saturday, April 27, 13

    View full-size slide

  189. @sandimetz Apr 2013
    Test
    Saturday, April 27, 13

    View full-size slide

  190. @sandimetz Apr 2013
    Test.
    Everything
    Saturday, April 27, 13

    View full-size slide

  191. @sandimetz Apr 2013
    Test
    .
    Everything.
    Once
    Saturday, April 27, 13

    View full-size slide

  192. @sandimetz Apr 2013
    Test the
    interface
    Saturday, April 27, 13

    View full-size slide

  193. @sandimetz Apr 2013
    Trust
    collaborators
    Saturday, April 27, 13

    View full-size slide

  194. @sandimetz Apr 2013
    Insist on
    simplicity
    Saturday, April 27, 13

    View full-size slide

  195. @sandimetz Apr 2013
    Practice the Tricks
    Saturday, April 27, 13

    View full-size slide

  196. @sandimetz Apr 2013
    Saturday, April 27, 13

    View full-size slide

  197. @sandimetz Apr 2013
    Saturday, April 27, 13

    View full-size slide

  198. @sandimetz Apr 2013
    I love my tests
    Saturday, April 27, 13

    View full-size slide

  199. Thanks
    Sandi Metz
    @sandimetz
    Saturday, April 27, 13

    View full-size slide

  200. 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
    Saturday, April 27, 13

    View full-size slide

  201. @sandimetz Apr 2013
    http://poodr.info
    Saturday, April 27, 13

    View full-size slide

  202. Questions?
    Saturday, April 27, 13

    View full-size slide

  203. @sandimetz Apr 2013
    http://poodr.info
    Saturday, April 27, 13

    View full-size slide