Testing Gone Right
  from RubyConf 2011 and others
      @jwo - Jesse Wolgamott
What We’ll Talk Abouts
What We’ll Talk Abouts

• Mock is not a 4 letter word
• Fast tests. Really Fast.
• Rails Models
Mock is not a 4 letter
        word
Mocks
Mocks

• Mocks are not stubs
Mocks

• Mocks are not stubs
• Mocks assert Messages
Mocks

• Mocks are not stubs
• Mocks assert Messages
• Assert on state => Stubbing
Mocks

• Mocks are not stubs
• Mocks assert Messages
• Assert on state => Stubbing
• Assert on messages => Mocking
Ruby + OOP + Mock
Ruby + OOP + Mock

• is goood
Ruby + OOP + Mock

• is goood
• Objects should tell objects to do things
Ruby + OOP + Mock

• is goood
• Objects should tell objects to do things
• (Tell don’t ask)
Ruby + OOP + Mock

• is goood
• Objects should tell objects to do things
• (Tell don’t ask)
• You tell to do things using messages
Mock Roles

• Instead of mocking objects, try to mock
  roles
• Wanting to mock concrete objects is a
  design smell
Example
Example

• Restaurant system reserves tables
• Instead of your interface talking to a
  ReservationService:
• Talk to ReservationInterfaceHandler
Message Example
 class ReservationInterface
   def initialize(handler)
     @handler = handler
   end

   def select_table(table)
     @table = table
   end

   def submit_request
     @handler.reserve(@table)
   end
 end
And to Spec it
describe ReservationInterface do

 let(:table){something_interesting_here}
 it "should tell the handler to reserve a table" do
   handler = double('reservation_handler')
   handler.should_receive(:reserve).with(table)
   machine = ReservationInterface.new(handler)
   machine.select_table(table)
   machine.submit_request
 end
Watch Out!
On APIs

• Only Mock Types you own
• Don’t mock boundary objects
• If you do, you’ll duplicate code -> CODE
  SMELL
Other Mock Smells
Other Mock Smells

• Having to setup more than 3 lines -- that’s a
  code smell
Other Mock Smells

• Having to setup more than 3 lines -- that’s a
  code smell
• Mocking concrete objects -- that’s a code
  smell
Other Mock Smells

• Having to setup more than 3 lines -- that’s a
  code smell
• Mocking concrete objects -- that’s a code
  smell
• When tests go brittle and developers
  declare mocks suck -- that’s a code smell
However
However
• If you have a “standard” Rails app,
  programmed procedurally:
However
• If you have a “standard” Rails app,
  programmed procedurally:
• Mocks are not for you
However
• If you have a “standard” Rails app,
  programmed procedurally:
• Mocks are not for you
• If you encapulate your code. If you hide
  information (OOP). If you use messages:
However
• If you have a “standard” Rails app,
  programmed procedurally:
• Mocks are not for you
• If you encapulate your code. If you hide
  information (OOP). If you use messages:
• Mocks are for you!
Mocks Don’t Suck
Mocks Don’t Suck
• If your code is hard to test, you need better
  code
Mocks Don’t Suck
• If your code is hard to test, you need better
  code
• If you have a test that is HARD to setup,
  your class (or test) is doing too much
Mocks Don’t Suck
• If your code is hard to test, you need better
  code
• If you have a test that is HARD to setup,
  your class (or test) is doing too much
• If you see tests with uber-mocks, the
  answer is not to delete mocks. It’s to
  isolate your tests
Fast Rails Tests
Out of the Box
Out of the Box

• Most Rails apps start as CRUD apps
Out of the Box

• Most Rails apps start as CRUD apps
• Tests start fast-ish
Out of the Box

• Most Rails apps start as CRUD apps
• Tests start fast-ish
• Like a frog, we don’t notice the increase
“fast” tests


• Tests should run in under a second
• The entire rspec test suite
How?


• Don’t load rails
Example
             Spec Run
                        Real Time
               Time

With Rails     .07 s      8.1 s


Without
             0.0007 s      .8 s
 Rails
Avoid Factories
Avoid Factories

• Instead, extract your code to classes with
  one single responsibility
Avoid Factories

• Instead, extract your code to classes with
  one single responsibility
• Rather than knowing everything about your
  object
Avoid Factories

• Instead, extract your code to classes with
  one single responsibility
• Rather than knowing everything about your
  object
• Or, load methods via modules
Rails Models
Rails Models

• Are Fat.
Rails Models

• Are Fat.
• Wait, isn’t that good?
Rails Models

• Are Fat.
• Wait, isn’t that good?
• It’s better than controller logic
Rails Models

• Are Fat.
• Wait, isn’t that good?
• It’s better than controller logic
• But no.
What then?
What then?

• Your models can have validations
What then?

• Your models can have validations
• Call out to policy or decision classes for
  business logic
What then?

• Your models can have validations
• Call out to policy or decision classes for
  business logic
• Use Ruby Classes
What then?

• Your models can have validations
• Call out to policy or decision classes for
  business logic
• Use Ruby Classes
• Use Presenters
Example

• Example of building a burrito that is
  delicious, and can know it’s delicious
• In Isolation: https://gist.github.com/1281093
• Using Modules: https://gist.github.com/
  1281064
Credits
Why you don’t get mock objects @gregmoeck

Your tests are lying to you        @chrismdp

Fast Rails talk at gogoruco 2011   @coreyhaines
Testing in Isolation.
                                   @garybernhardt
destroyalldoftware.com

Testing gone-right

  • 1.
    Testing Gone Right from RubyConf 2011 and others @jwo - Jesse Wolgamott
  • 2.
  • 3.
    What We’ll TalkAbouts • Mock is not a 4 letter word • Fast tests. Really Fast. • Rails Models
  • 4.
    Mock is nota 4 letter word
  • 5.
  • 6.
  • 7.
    Mocks • Mocks arenot stubs • Mocks assert Messages
  • 8.
    Mocks • Mocks arenot stubs • Mocks assert Messages • Assert on state => Stubbing
  • 9.
    Mocks • Mocks arenot stubs • Mocks assert Messages • Assert on state => Stubbing • Assert on messages => Mocking
  • 10.
    Ruby + OOP+ Mock
  • 11.
    Ruby + OOP+ Mock • is goood
  • 12.
    Ruby + OOP+ Mock • is goood • Objects should tell objects to do things
  • 13.
    Ruby + OOP+ Mock • is goood • Objects should tell objects to do things • (Tell don’t ask)
  • 14.
    Ruby + OOP+ Mock • is goood • Objects should tell objects to do things • (Tell don’t ask) • You tell to do things using messages
  • 15.
    Mock Roles • Insteadof mocking objects, try to mock roles • Wanting to mock concrete objects is a design smell
  • 16.
  • 17.
    Example • Restaurant systemreserves tables • Instead of your interface talking to a ReservationService: • Talk to ReservationInterfaceHandler
  • 18.
    Message Example classReservationInterface def initialize(handler) @handler = handler end def select_table(table) @table = table end def submit_request @handler.reserve(@table) end end
  • 19.
    And to Specit describe ReservationInterface do let(:table){something_interesting_here} it "should tell the handler to reserve a table" do handler = double('reservation_handler') handler.should_receive(:reserve).with(table) machine = ReservationInterface.new(handler) machine.select_table(table) machine.submit_request end
  • 20.
  • 21.
    On APIs • OnlyMock Types you own • Don’t mock boundary objects • If you do, you’ll duplicate code -> CODE SMELL
  • 22.
  • 23.
    Other Mock Smells •Having to setup more than 3 lines -- that’s a code smell
  • 24.
    Other Mock Smells •Having to setup more than 3 lines -- that’s a code smell • Mocking concrete objects -- that’s a code smell
  • 25.
    Other Mock Smells •Having to setup more than 3 lines -- that’s a code smell • Mocking concrete objects -- that’s a code smell • When tests go brittle and developers declare mocks suck -- that’s a code smell
  • 26.
  • 27.
    However • If youhave a “standard” Rails app, programmed procedurally:
  • 28.
    However • If youhave a “standard” Rails app, programmed procedurally: • Mocks are not for you
  • 29.
    However • If youhave a “standard” Rails app, programmed procedurally: • Mocks are not for you • If you encapulate your code. If you hide information (OOP). If you use messages:
  • 30.
    However • If youhave a “standard” Rails app, programmed procedurally: • Mocks are not for you • If you encapulate your code. If you hide information (OOP). If you use messages: • Mocks are for you!
  • 31.
  • 32.
    Mocks Don’t Suck •If your code is hard to test, you need better code
  • 33.
    Mocks Don’t Suck •If your code is hard to test, you need better code • If you have a test that is HARD to setup, your class (or test) is doing too much
  • 34.
    Mocks Don’t Suck •If your code is hard to test, you need better code • If you have a test that is HARD to setup, your class (or test) is doing too much • If you see tests with uber-mocks, the answer is not to delete mocks. It’s to isolate your tests
  • 35.
  • 36.
  • 37.
    Out of theBox • Most Rails apps start as CRUD apps
  • 38.
    Out of theBox • Most Rails apps start as CRUD apps • Tests start fast-ish
  • 39.
    Out of theBox • Most Rails apps start as CRUD apps • Tests start fast-ish • Like a frog, we don’t notice the increase
  • 40.
    “fast” tests • Testsshould run in under a second • The entire rspec test suite
  • 41.
  • 42.
    Example Spec Run Real Time Time With Rails .07 s 8.1 s Without 0.0007 s .8 s Rails
  • 43.
  • 44.
    Avoid Factories • Instead,extract your code to classes with one single responsibility
  • 45.
    Avoid Factories • Instead,extract your code to classes with one single responsibility • Rather than knowing everything about your object
  • 46.
    Avoid Factories • Instead,extract your code to classes with one single responsibility • Rather than knowing everything about your object • Or, load methods via modules
  • 47.
  • 48.
  • 49.
    Rails Models • AreFat. • Wait, isn’t that good?
  • 50.
    Rails Models • AreFat. • Wait, isn’t that good? • It’s better than controller logic
  • 51.
    Rails Models • AreFat. • Wait, isn’t that good? • It’s better than controller logic • But no.
  • 52.
  • 53.
    What then? • Yourmodels can have validations
  • 54.
    What then? • Yourmodels can have validations • Call out to policy or decision classes for business logic
  • 55.
    What then? • Yourmodels can have validations • Call out to policy or decision classes for business logic • Use Ruby Classes
  • 56.
    What then? • Yourmodels can have validations • Call out to policy or decision classes for business logic • Use Ruby Classes • Use Presenters
  • 57.
    Example • Example ofbuilding a burrito that is delicious, and can know it’s delicious • In Isolation: https://gist.github.com/1281093 • Using Modules: https://gist.github.com/ 1281064
  • 58.
    Credits Why you don’tget mock objects @gregmoeck Your tests are lying to you @chrismdp Fast Rails talk at gogoruco 2011 @coreyhaines Testing in Isolation. @garybernhardt destroyalldoftware.com