Don’t Mock
Yourself Out

  David Chelimsky
 Articulated Man, Inc
http://martinfowler.com/articles/mocksArentStubs.html
Classical and
Mockist Testing
Classical and
Mockist Testing
Classical and
Mockist Testing
classicist   mockist
merbist   railsist
rspecist testunitist
ist bin ein
red herring
The big issue here
      is when to use a
           mock
http://martinfowler.com/articles/mocksArentStubs.html
agenda
๏ overview of stubs and mocks
๏ mocks/stubs applied to rails
๏ guidelines and pitfalls
๏ questions
test double
test stub
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerqu...
test stub
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerqu...
test stub
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerqu...
test stub
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerqu...
mock object
describe Statement do
  it quot;logs a message when printedquot; do
    customer = stub(quot;customerquot;)
  ...
mock object
describe Statement do
  it quot;logs a message when printedquot; do
    customer = stub(quot;customerquot;)
  ...
mock object
describe Statement do
  it quot;logs a message when printedquot; do
    customer = stub(quot;customerquot;)
  ...
mock object
describe Statement do
  it quot;logs a message when printedquot; do
    customer = stub(quot;customerquot;)
  ...
mock object
describe Statement do
  it quot;logs a message when printedquot; do
    customer = stub(quot;customerquot;)
  ...
method level
 concepts
describe Statement do
  it quot;logs a message when printedquot; do
    customer = Object.new
    logger   = Object.new
  ...
describe Statement do
  it quot;logs a message when printedquot; do
    customer = Object.new
    logger   = Object.new
  ...
describe Statement do
  it quot;logs a message when printedquot; do
    customer = Object.new
    logger   = Object.new
  ...
method stub
describe Statement do
  it quot;logs a message when printedquot; do
    customer = Object.new
    logger   = O...
describe Statement do
  it quot;logs a message when printedquot; do
    customer = Object.new
    logger   = Object.new
  ...
message expectation
 describe Statement do
   it quot;logs a message when printedquot; do
     customer = Object.new
     ...
things aren’t always
    as they seem
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = mock(quot;customerquot;)
    s...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = mock(quot;customerquot;)
    s...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = mock(quot;customerquot;)
    s...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = mock(quot;customerquot;)
    s...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = mock(quot;customerquot;)
    s...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = mock(quot;customerquot;)
    s...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
describe Statement do
  it quot;uses the customer name in the headerquot; do
    customer = stub(quot;customerquot;)
    c...
stubs are often
used like mocks
mocks are often
used like stubs
we verify stubs by
checking state after
     an action
we tell mocks to
verify interactions
sometimes stubs
  just make the
   system run
when are
method stubs
  helpful?
isolation from
non-determinism
random values
random values
class BoardTest < MiniTest::Unit::TestCase
  def test_allows_move_to_last_square
    board = Board.new(
    ...
time
describe Event do
  it quot;is not happening before the start timequot; do
    now = Time.now
    start = now + 1
   ...
isolation from
external dependencies
network access

          Database
          Interface   Database

Subject

          Network
                      Intern...
network access
def test_successful_purchase_sends_shipping_message
  ActiveMerchant::Billing::Base.mode = :test
  gateway ...
network access

                      Stub
                    Database
 Code     Subject
Example
                     Stu...
network access
def test_successful_purchase_sends_shipping_message
  gateway = stub()
  gateway.stubs(:authorize).returns(...
polymorphic
collaborators
strategies
describe Employee do
  it quot;delegates pay() to payment strategyquot; do
    payment_strategy = mock()
    em...
mixins/plugins
describe AgeIdentifiable do
  describe quot;#can_vote?quot; do
    it quot;raises if including does not res...
when are
message expectations
      helpful?
side effects
describe Statement do
  it quot;logs a message when printedquot; do
    customer = stub(quot;customerquot;)
 ...
caching
describe ZipCode do
  it quot;should only validate oncequot; do
    validator = mock()
    zipcode = ZipCode.new(q...
interface discovery
describe quot;thing I'm working onquot; do
  it quot;does something with some assistancequot; do
    t...
isolation testing
specifying/testing
    individual
objects in isolation
good fit with lots of
   little objects
all of these
concepts apply to
   the non-rails
 specific parts of
  our rails apps
isolation testing the
 rails-specific parts
of our applicationss
V
M

    C
View
Controller
 Model
Browser
  Router
   View
Controller
  Model
Database
rails testing
๏ unit tests
๏ functional tests
๏ integration tests
rails unit tests
๏ model classes (repositories)
๏ model objects
๏ database
rails functional tests
๏ model classes (repositories)
๏ model objects
๏ database
๏ views
๏ controllers
rails functional tests
๏ model classes (repositories)
๏ model objects
๏ database
๏ views
๏ controllers
rails functional tests
๏ model classes (repositories)
๏ model objects

                           !DRY
๏ database
๏ views
...
rails integration tests
๏ model classes (repositories)
๏ model objects
๏ database
๏ views
๏ controllers
๏ routing/sessions
rails integration tests
๏ model classes (repositories)
๏ model objects
๏ database

                             !DRY
๏ vie...
the BDD approach
inherited from XP
customer specs




developer specs
rails integration tests
        + webrat




  shoulda, context,
   micronaut, etc
customer specs are
  implemented as
  end to end tests
developer specs
are implemented as
   isolation tests
mocking and
 stubbing
 with rails
partials in view specs
describe quot;/registrations/new.html.erbquot; do
  before(:each) do
    template.stub(:render).wit...
conditional branches in
   controller specs
 describe quot;POST createquot; do
   describe quot;with valid attributesquot;...
conditional branches in
   controller specs
 describe quot;POST createquot; do
   describe quot;with invalid attributesquo...
conditional branches in
   controller specs
 describe quot;POST createquot; do
   describe quot;with invalid attributesquo...
shave a few lines but
leave a little stubble




  http://github.com/dchelimsky/stubble
stubble
describe quot;POST createquot; do
  describe quot;with valid attributesquot; do
    it quot;redirects to list of r...
stubble
describe quot;POST createquot; do
  describe quot;with invalid attributesquot; do
    it quot;re-renders the new f...
chains
describe UsersController do
  it quot;GET 'best_friend'quot; do
    member = stub_model(User)
    friends = stub()
...
chains
describe UsersController do
  it quot;GET 'best_friend'quot; do
    friend = stub_model(User)
    User.stub_chain(:...
guidlines, pitfalls, and
 common concerns
focus on roles
  Mock Roles, not Objects
  http://www.jmock.org/oopsla2004.pdf
keep things simple
avoid tight coupling
complex setup is a
red flag for design
       issues
don’t stub/mock the
object you’re testing
impedes
refactoring
:refactoring => <<-DEFINITION

    Improving design
        without
   changing behaviour

DEFINITION
what is behaviour?
false positives
describe RegistrationsController do
  describe quot;GET 'pending'quot; do
    it quot;finds the pending re...
false positives
describe RegistrationsController do
  describe quot;GET 'pending'quot; do
    it quot;finds the pending re...
false positives
describe Registration do
  describe quot;#pendingquot; do
    it quot;finds pending registrationsquot; do
...
false positives
describe Registration do
  describe quot;#pendingquot; do
    it quot;finds pending registrationsquot; do
...
false positives
describe Registration do
  describe quot;#pendingquot; do
    it quot;finds pending registrationsquot; do
...
false positives
describe Registration do
  describe quot;#pendingquot; do
    it quot;finds pending registrationsquot; do
...
cucumber
http://pragprog.com/titles/achbd/the-rspec-book




                                                        http://xunitpa...
http://blog.davidchelimsky.net/
         http://www.articulatedman.com/
                  http://rspec.info/
             ...
ruby frameworks
rspec-mocks

http://github.com/dchelimsky/rspec
mocha

http://github.com/floehopper/mocha
flexmock

http://github.com/jimweirich/flexmock
rr

http://github.com/btakita/rr
not a mock

http://github.com/notahat/not_a_mock
Don T Mock Yourself Out Presentation
Don T Mock Yourself Out Presentation
Don T Mock Yourself Out Presentation
Don T Mock Yourself Out Presentation
Don T Mock Yourself Out Presentation
Don T Mock Yourself Out Presentation
Don T Mock Yourself Out Presentation
Upcoming SlideShare
Loading in...5
×

Don T Mock Yourself Out Presentation

1,675

Published on

Published in: Business, Education
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,675
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
46
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

Transcript of "Don T Mock Yourself Out Presentation"

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

    Clipping is a handy way to collect important slides you want to go back to later.

×