Don T Mock Yourself Out Presentation

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    2 Favorites

    Don T Mock Yourself Out Presentation - Presentation Transcript

    1. Don’t Mock Yourself Out David Chelimsky Articulated Man, Inc
    2. http://martinfowler.com/articles/mocksArentStubs.html
    3. Classical and Mockist Testing
    4. Classical and Mockist Testing
    5. Classical and Mockist Testing
    6. classicist mockist
    7. merbist railsist
    8. rspecist testunitist
    9. ist bin ein red herring
    10. The big issue here is when to use a mock http://martinfowler.com/articles/mocksArentStubs.html
    11. agenda ๏ overview of stubs and mocks ๏ mocks/stubs applied to rails ๏ guidelines and pitfalls ๏ questions
    12. test double
    13. test stub describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end
    14. test stub describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end
    15. test stub describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end
    16. test stub describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end
    17. mock object describe Statement do it \"logs a message when printed\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') logger = mock(\"logger\") statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    18. mock object describe Statement do it \"logs a message when printed\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') logger = mock(\"logger\") statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    19. mock object describe Statement do it \"logs a message when printed\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') logger = mock(\"logger\") statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    20. mock object describe Statement do it \"logs a message when printed\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') logger = mock(\"logger\") statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    21. mock object describe Statement do it \"logs a message when printed\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') logger = mock(\"logger\") statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    22. method level concepts
    23. describe Statement do it \"logs a message when printed\" do customer = Object.new logger = Object.new customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    24. describe Statement do it \"logs a message when printed\" do customer = Object.new logger = Object.new customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    25. describe Statement do it \"logs a message when printed\" do customer = Object.new logger = Object.new customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    26. method stub describe Statement do it \"logs a message when printed\" do customer = Object.new logger = Object.new customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    27. describe Statement do it \"logs a message when printed\" do customer = Object.new logger = Object.new customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    28. message expectation describe Statement do it \"logs a message when printed\" do customer = Object.new logger = Object.new customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    29. things aren’t always as they seem
    30. describe Statement do it \"uses the customer name in the header\" do customer = mock(\"customer\") statement = Statement.new(customer) customer.should_receive(:name).and_return('Joe Customer') statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    31. describe Statement do it \"uses the customer name in the header\" do customer = mock(\"customer\") statement = Statement.new(customer) customer.should_receive(:name).and_return('Joe Customer') statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    32. describe Statement do it \"uses the customer name in the header\" do customer = mock(\"customer\") statement = Statement.new(customer) customer.should_receive(:name).and_return('Joe Customer') statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    33. describe Statement do it \"uses the customer name in the header\" do customer = mock(\"customer\") statement = Statement.new(customer) customer.should_receive(:name).and_return('Joe Customer') statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    34. describe Statement do it \"uses the customer name in the header\" do customer = mock(\"customer\") statement = Statement.new(customer) customer.should_receive(:name).and_return('Joe Customer') statement.header.should == \"Statement for Joe Customer\" end end message expectation class Statement def header \"Statement for #{@customer.name}\" end end
    35. describe Statement do it \"uses the customer name in the header\" do customer = mock(\"customer\") statement = Statement.new(customer) customer.should_receive(:name).and_return('Joe Customer') statement.header.should == \"Statement for Joe Customer\" end end bound to implementation class Statement def header \"Statement for #{@customer.name}\" end end
    36. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    37. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    38. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    39. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end class Statement def header \"Statement for #{@customer.name}\" end end
    40. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end ???? class Statement def header \"Statement for #{@customer.name}\" end end
    41. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end message expectation class Statement def header \"Statement for #{@customer.name}\" end end
    42. describe Statement do it \"uses the customer name in the header\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') statement = Statement.new(customer) statement.header.should == \"Statement for Joe Customer\" end end bound to implementation class Statement def header \"Statement for #{@customer.name}\" end end
    43. stubs are often used like mocks
    44. mocks are often used like stubs
    45. we verify stubs by checking state after an action
    46. we tell mocks to verify interactions
    47. sometimes stubs just make the system run
    48. when are method stubs helpful?
    49. isolation from non-determinism
    50. random values
    51. random values class BoardTest < MiniTest::Unit::TestCase def test_allows_move_to_last_square board = Board.new( :squares => 50, :die => MiniTest::Mock.new.expect('roll', 2) ) piece = Piece.new board.place(piece, 48) board.move(piece) assert board.squares[48].contains?(piece) end end
    52. time describe Event do it \"is not happening before the start time\" do now = Time.now start = now + 1 Time.stub(:now).and_return now event = Event.new(:start => start) event.should_not be_happening end end
    53. isolation from external dependencies
    54. network access Database Interface Database Subject Network Internets Interface
    55. network access def test_successful_purchase_sends_shipping_message ActiveMerchant::Billing::Base.mode = :test gateway = ActiveMerchant::Billing::TrustCommerceGateway.new( :login => 'TestMerchant', :password => 'password' ) item = stub() messenger = mock() messenger.expects(:ship).with(item) purchase = Purchase.new(gateway, item, credit_card, messenger) purchase.finalize end
    56. network access Stub Database Code Subject Example Stub Network
    57. network access def test_successful_purchase_sends_shipping_message gateway = stub() gateway.stubs(:authorize).returns( ActiveMerchant::Billing::Response.new(true, \"ignore\") ) item = stub() messenger = mock() messenger.expects(:ship).with(item) purchase = Purchase.new(gateway, item, credit_card, messenger) purchase.finalize end
    58. polymorphic collaborators
    59. strategies describe Employee do it \"delegates pay() to payment strategy\" do payment_strategy = mock() employee = Employee.new(payment_strategy) payment_strategy.expects(:pay) employee.pay end end
    60. mixins/plugins describe AgeIdentifiable do describe \"#can_vote?\" do it \"raises if including does not respond to birthdate\" do object = Object.new object.extend AgeIdentifiable expect { object.can_vote? }.to raise_error( /must supply a birthdate/ ) end it \"returns true if birthdate == 18 years ago\" do object = Object.new stub(object).birthdate {18.years.ago.to_date} object.extend AgeIdentifiable object.can_vote?.should be(true) end end end
    61. when are message expectations helpful?
    62. side effects describe Statement do it \"logs a message when printed\" do customer = stub(\"customer\") customer.stub(:name).and_return('Joe Customer') logger = mock(\"logger\") statement = Statement.new(customer, logger) logger.should_receive(:log).with(/Joe Customer/) statement.print end end
    63. caching describe ZipCode do it \"should only validate once\" do validator = mock() zipcode = ZipCode.new(\"02134\", validator) validator.should_receive(:valid?).with(\"02134\").once. and_return(true) zipcode.valid? zipcode.valid? end end
    64. interface discovery describe \"thing I'm working on\" do it \"does something with some assistance\" do thing_i_need = mock() thing_i_am_working_on = ThingIAmWorkingOn.new(thing_i_need) thing_i_need.should_receive(:help_me).and_return('what I need') thing_i_am_working_on.do_something_complicated end end
    65. isolation testing
    66. specifying/testing individual objects in isolation
    67. good fit with lots of little objects
    68. all of these concepts apply to the non-rails specific parts of our rails apps
    69. isolation testing the rails-specific parts of our applicationss
    70. V M C
    71. View Controller Model
    72. Browser Router View Controller Model Database
    73. rails testing ๏ unit tests ๏ functional tests ๏ integration tests
    74. rails unit tests ๏ model classes (repositories) ๏ model objects ๏ database
    75. rails functional tests ๏ model classes (repositories) ๏ model objects ๏ database ๏ views ๏ controllers
    76. rails functional tests ๏ model classes (repositories) ๏ model objects ๏ database ๏ views ๏ controllers
    77. rails functional tests ๏ model classes (repositories) ๏ model objects !DRY ๏ database ๏ views ๏ controllers
    78. rails integration tests ๏ model classes (repositories) ๏ model objects ๏ database ๏ views ๏ controllers ๏ routing/sessions
    79. rails integration tests ๏ model classes (repositories) ๏ model objects ๏ database !DRY ๏ views ๏ controllers ๏ routing/sessions
    80. the BDD approach
    81. inherited from XP
    82. customer specs developer specs
    83. rails integration tests + webrat shoulda, context, micronaut, etc
    84. customer specs are implemented as end to end tests
    85. developer specs are implemented as isolation tests
    86. mocking and stubbing with rails
    87. partials in view specs describe \"/registrations/new.html.erb\" do before(:each) do template.stub(:render).with(:partial => anything) end it \"renders the registration navigation\" do template.should_receive(:render).with(:partial => 'nav') render end it \"renders the registration form \" do template.should_receive(:render).with(:partial => 'form') render end end
    88. conditional branches in controller specs describe \"POST create\" do describe \"with valid attributes\" do it \"redirects to list of registrations\" do registration = stub_model(Registration) Registration.stub(:new).and_return(registration) registration.stub(:save!).and_return(true) post 'create' response.should redirect_to(registrations_path) end end end
    89. conditional branches in controller specs describe \"POST create\" do describe \"with invalid attributes\" do it \"re-renders the new form\" do registration = stub_model(Registration) Registration.stub(:new).and_return(registration) registration.stub(:save!).and_raise( ActiveRecord::RecordInvalid.new(registration)) post 'create' response.should render_template('new') end end end
    90. conditional branches in controller specs describe \"POST create\" do describe \"with invalid attributes\" do it \"assigns the registration\" do registration = stub_model(Registration) Registration.stub(:new).and_return(registration) registration.stub(:save!).and_raise( ActiveRecord::RecordInvalid.new(registration)) post 'create' assigns[:registration].should equal(registration) end end end
    91. shave a few lines but leave a little stubble http://github.com/dchelimsky/stubble
    92. stubble describe \"POST create\" do describe \"with valid attributes\" do it \"redirects to list of registrations\" do stubbing(Registration) do post 'create' response.should redirect_to(registrations_path) end end end end
    93. stubble describe \"POST create\" do describe \"with invalid attributes\" do it \"re-renders the new form\" do stubbing(Registration, :as => :invalid) do post 'create' response.should render_template('new') end end it \"assigns the registration\" do stubbing(Registration, :as => :invalid) do |registration| post 'create' assigns[:registration].should equal(registration) end end end end
    94. chains describe UsersController do it \"GET 'best_friend'\" do member = stub_model(User) friends = stub() friend = stub_model(User) User.stub(:find).and_return(member) member.stub(:friends).and_return(friends) friends.stub(:favorite).and_return(friend) get :best_friend, :id => '37' assigns[:friend].should equal(friend) end end
    95. chains describe UsersController do it \"GET 'best_friend'\" do friend = stub_model(User) User.stub_chain(:find, :friends, :favorite). and_return(friend) get :best_friend, :id => '37' assigns[:friend].should equal(friend) end end
    96. guidlines, pitfalls, and common concerns
    97. focus on roles Mock Roles, not Objects http://www.jmock.org/oopsla2004.pdf
    98. keep things simple
    99. avoid tight coupling
    100. complex setup is a red flag for design issues
    101. don’t stub/mock the object you’re testing
    102. impedes refactoring
    103. :refactoring => <<-DEFINITION Improving design without changing behaviour DEFINITION
    104. what is behaviour?
    105. false positives describe RegistrationsController do describe \"GET 'pending'\" do it \"finds the pending registrations\" do pending_registration = stub_model(Registration) Registration.should_receive(:pending). and_return([pending_registration]) get 'pending' assigns[:registrations].should == [pending_registration] end end end class RegistrationsController < ApplicationController def pending @registrations = Registration.pending end end
    106. false positives describe RegistrationsController do describe \"GET 'pending'\" do it \"finds the pending registrations\" do pending_registration = stub_model(Registration) Registration.should_receive(:pending). and_return([pending_registration]) get 'pending' assigns[:registrations].should == [pending_registration] end end end class RegistrationsController < ApplicationController def pending @registrations = Registration.pending end end
    107. false positives describe Registration do describe \"#pending\" do it \"finds pending registrations\" do Registration.create! Registration.create!(:pending => true) Registration.pending.should have(1).item end end end class Registration < ActiveRecord::Base named_scope :pending, :conditions => {:pending => true} end
    108. false positives describe Registration do describe \"#pending\" do it \"finds pending registrations\" do Registration.create! Registration.create!(:pending => true) Registration.pending.should have(1).item end end end class Registration < ActiveRecord::Base named_scope :pending, :conditions => {:pending => true} end
    109. false positives describe Registration do describe \"#pending\" do it \"finds pending registrations\" do Registration.create! Registration.create!(:pending => true) Registration.pending_confirmation.should have(1).item end end end class Registration < ActiveRecord::Base named_scope :pending_confirmation, :conditions => {:pending => true} end
    110. false positives describe Registration do describe \"#pending\" do it \"finds pending registrations\" do Registration.create! Registration.create!(:pending => true) Registration.pending_confirmation.should have(1).item end end end class Registration < ActiveRecord::Base named_scope :pending_confirmation, :conditions => {:pending => true} end
    111. cucumber
    112. http://pragprog.com/titles/achbd/the-rspec-book http://xunitpatterns.com/ growing object-oriented Mock Roles, not Objects software, guided by tests http://www.jmock.org/oopsla2004.pdf http://www.mockobjects.com/book/
    113. http://blog.davidchelimsky.net/ http://www.articulatedman.com/ http://rspec.info/ http://cukes.info/ http://pragprog.com/titles/achbd/the-rspec-book
    114. ruby frameworks
    115. rspec-mocks http://github.com/dchelimsky/rspec
    116. mocha http://github.com/floehopper/mocha
    117. flexmock http://github.com/jimweirich/flexmock
    118. rr http://github.com/btakita/rr
    119. not a mock http://github.com/notahat/not_a_mock

    + railsconfrailsconf, 6 months ago

    custom

    637 views, 2 favs, 0 embeds more stats

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 637
      • 637 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 2
    • Downloads 15
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories