SlideShare une entreprise Scribd logo
1  sur  126
Télécharger pour lire hors ligne
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;customerquot;)
    customer.stub(:name).and_return('Joe Customer')
    statement = Statement.new(customer)

    statement.header.should == quot;Statement for Joe Customerquot;
  end
end
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
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
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
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
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
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
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
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
method level
 concepts
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
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
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
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
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
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
things aren’t always
    as they seem
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
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
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
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
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
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
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
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
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
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
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
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
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
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(
      :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
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
isolation from
external dependencies
network access

          Database
          Interface   Database

Subject

          Network
                      Internets
          Interface
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
network access

                      Stub
                    Database
 Code     Subject
Example
                     Stub
                    Network
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
polymorphic
collaborators
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
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
when are
message expectations
      helpful?
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
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
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
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
๏ controllers
rails integration tests
๏ model classes (repositories)
๏ model objects
๏ database
๏ views
๏ controllers
๏ routing/sessions
rails integration tests
๏ model classes (repositories)
๏ model objects
๏ database

                             !DRY
๏ views
๏ controllers
๏ routing/sessions
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).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
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
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
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
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 registrationsquot; do
      stubbing(Registration) do
        post 'create'
        response.should redirect_to(registrations_path)
      end
    end
  end
end
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
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
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
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 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
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
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
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
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
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
cucumber
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/
http://blog.davidchelimsky.net/
         http://www.articulatedman.com/
                  http://rspec.info/
                 http://cukes.info/
http://pragprog.com/titles/achbd/the-rspec-book
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

Contenu connexe

Similaire à Don T Mock Yourself Out Presentation

Library Website
Library WebsiteLibrary Website
Library Websitegholtron
 
Having issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdfHaving issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdfrajkumarm401
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
Ruby 程式語言簡介
Ruby 程式語言簡介Ruby 程式語言簡介
Ruby 程式語言簡介Wen-Tien Chang
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails Mohit Jain
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?brynary
 
Using REST and XML Builder for legacy XML
Using REST and XML Builder for legacy XMLUsing REST and XML Builder for legacy XML
Using REST and XML Builder for legacy XMLKeith Pitty
 
1582627
15826271582627
1582627tabish
 
List Processing in ABAP
List Processing in ABAPList Processing in ABAP
List Processing in ABAPsapdocs. info
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Storiesrahoulb
 
4. Метапрограмиране
4. Метапрограмиране4. Метапрограмиране
4. МетапрограмиранеStefan Kanev
 
e computer notes - Producing readable output with i sql plus
e computer notes - Producing readable output with i sql pluse computer notes - Producing readable output with i sql plus
e computer notes - Producing readable output with i sql plusecomputernotes
 
怖くない!Implicit!
怖くない!Implicit!怖くない!Implicit!
怖くない!Implicit!HonMarkHunt
 

Similaire à Don T Mock Yourself Out Presentation (20)

Testing Merb
Testing MerbTesting Merb
Testing Merb
 
Factory Girl
Factory GirlFactory Girl
Factory Girl
 
Library Website
Library WebsiteLibrary Website
Library Website
 
Having issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdfHaving issues with passing my values through different functions aft.pdf
Having issues with passing my values through different functions aft.pdf
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
Javascript
JavascriptJavascript
Javascript
 
Ruby 程式語言簡介
Ruby 程式語言簡介Ruby 程式語言簡介
Ruby 程式語言簡介
 
TDD, BDD and mocks
TDD, BDD and mocksTDD, BDD and mocks
TDD, BDD and mocks
 
Functional Web Development using Elm
Functional Web Development using ElmFunctional Web Development using Elm
Functional Web Development using Elm
 
SQl
SQlSQl
SQl
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
 
Using REST and XML Builder for legacy XML
Using REST and XML Builder for legacy XMLUsing REST and XML Builder for legacy XML
Using REST and XML Builder for legacy XML
 
1582627
15826271582627
1582627
 
List Processing in ABAP
List Processing in ABAPList Processing in ABAP
List Processing in ABAP
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Stories
 
4. Метапрограмиране
4. Метапрограмиране4. Метапрограмиране
4. Метапрограмиране
 
dfhdf
dfhdfdfhdf
dfhdf
 
e computer notes - Producing readable output with i sql plus
e computer notes - Producing readable output with i sql pluse computer notes - Producing readable output with i sql plus
e computer notes - Producing readable output with i sql plus
 
怖くない!Implicit!
怖くない!Implicit!怖くない!Implicit!
怖くない!Implicit!
 

Plus de railsconf

Smacking Git Around Advanced Git Tricks
Smacking Git Around   Advanced Git TricksSmacking Git Around   Advanced Git Tricks
Smacking Git Around Advanced Git Tricksrailsconf
 
Running The Show Configuration Management With Chef Presentation
Running The Show  Configuration Management With Chef PresentationRunning The Show  Configuration Management With Chef Presentation
Running The Show Configuration Management With Chef Presentationrailsconf
 
Sd208 Ds%2 C0
Sd208 Ds%2 C0Sd208 Ds%2 C0
Sd208 Ds%2 C0railsconf
 
Rails 3 And The Real Secret To High Productivity Presentation
Rails 3 And The Real Secret To High Productivity PresentationRails 3 And The Real Secret To High Productivity Presentation
Rails 3 And The Real Secret To High Productivity Presentationrailsconf
 
Quality Code With Cucumber Presentation
Quality Code With Cucumber PresentationQuality Code With Cucumber Presentation
Quality Code With Cucumber Presentationrailsconf
 
J Ruby On Rails Presentation
J Ruby On Rails PresentationJ Ruby On Rails Presentation
J Ruby On Rails Presentationrailsconf
 
Gov 2 0 Transparency Collaboration And Participation In Practice Presentation
Gov 2 0  Transparency  Collaboration  And Participation In Practice PresentationGov 2 0  Transparency  Collaboration  And Participation In Practice Presentation
Gov 2 0 Transparency Collaboration And Participation In Practice Presentationrailsconf
 
Crate Packaging Standalone Ruby Applications
Crate  Packaging Standalone Ruby ApplicationsCrate  Packaging Standalone Ruby Applications
Crate Packaging Standalone Ruby Applicationsrailsconf
 
Develop With Pleasure Deploy With Fun Glass Fish And Net Beans For A Better...
Develop With Pleasure  Deploy With Fun  Glass Fish And Net Beans For A Better...Develop With Pleasure  Deploy With Fun  Glass Fish And Net Beans For A Better...
Develop With Pleasure Deploy With Fun Glass Fish And Net Beans For A Better...railsconf
 
Building A Mini Google High Performance Computing In Ruby
Building A Mini Google  High Performance Computing In RubyBuilding A Mini Google  High Performance Computing In Ruby
Building A Mini Google High Performance Computing In Rubyrailsconf
 
A Z Introduction To Ruby On Rails
A Z Introduction To Ruby On RailsA Z Introduction To Ruby On Rails
A Z Introduction To Ruby On Railsrailsconf
 
The Even Darker Art Of Rails Engines Presentation
The Even Darker Art Of Rails Engines PresentationThe Even Darker Art Of Rails Engines Presentation
The Even Darker Art Of Rails Engines Presentationrailsconf
 
Below And Beneath Tdd Test Last Development And Other Real World Test Patter...
Below And Beneath Tdd  Test Last Development And Other Real World Test Patter...Below And Beneath Tdd  Test Last Development And Other Real World Test Patter...
Below And Beneath Tdd Test Last Development And Other Real World Test Patter...railsconf
 

Plus de railsconf (13)

Smacking Git Around Advanced Git Tricks
Smacking Git Around   Advanced Git TricksSmacking Git Around   Advanced Git Tricks
Smacking Git Around Advanced Git Tricks
 
Running The Show Configuration Management With Chef Presentation
Running The Show  Configuration Management With Chef PresentationRunning The Show  Configuration Management With Chef Presentation
Running The Show Configuration Management With Chef Presentation
 
Sd208 Ds%2 C0
Sd208 Ds%2 C0Sd208 Ds%2 C0
Sd208 Ds%2 C0
 
Rails 3 And The Real Secret To High Productivity Presentation
Rails 3 And The Real Secret To High Productivity PresentationRails 3 And The Real Secret To High Productivity Presentation
Rails 3 And The Real Secret To High Productivity Presentation
 
Quality Code With Cucumber Presentation
Quality Code With Cucumber PresentationQuality Code With Cucumber Presentation
Quality Code With Cucumber Presentation
 
J Ruby On Rails Presentation
J Ruby On Rails PresentationJ Ruby On Rails Presentation
J Ruby On Rails Presentation
 
Gov 2 0 Transparency Collaboration And Participation In Practice Presentation
Gov 2 0  Transparency  Collaboration  And Participation In Practice PresentationGov 2 0  Transparency  Collaboration  And Participation In Practice Presentation
Gov 2 0 Transparency Collaboration And Participation In Practice Presentation
 
Crate Packaging Standalone Ruby Applications
Crate  Packaging Standalone Ruby ApplicationsCrate  Packaging Standalone Ruby Applications
Crate Packaging Standalone Ruby Applications
 
Develop With Pleasure Deploy With Fun Glass Fish And Net Beans For A Better...
Develop With Pleasure  Deploy With Fun  Glass Fish And Net Beans For A Better...Develop With Pleasure  Deploy With Fun  Glass Fish And Net Beans For A Better...
Develop With Pleasure Deploy With Fun Glass Fish And Net Beans For A Better...
 
Building A Mini Google High Performance Computing In Ruby
Building A Mini Google  High Performance Computing In RubyBuilding A Mini Google  High Performance Computing In Ruby
Building A Mini Google High Performance Computing In Ruby
 
A Z Introduction To Ruby On Rails
A Z Introduction To Ruby On RailsA Z Introduction To Ruby On Rails
A Z Introduction To Ruby On Rails
 
The Even Darker Art Of Rails Engines Presentation
The Even Darker Art Of Rails Engines PresentationThe Even Darker Art Of Rails Engines Presentation
The Even Darker Art Of Rails Engines Presentation
 
Below And Beneath Tdd Test Last Development And Other Real World Test Patter...
Below And Beneath Tdd  Test Last Development And Other Real World Test Patter...Below And Beneath Tdd  Test Last Development And Other Real World Test Patter...
Below And Beneath Tdd Test Last Development And Other Real World Test Patter...
 

Dernier

Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deckPitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deckHajeJanKamps
 
Marketplace and Quality Assurance Presentation - Vincent Chirchir
Marketplace and Quality Assurance Presentation - Vincent ChirchirMarketplace and Quality Assurance Presentation - Vincent Chirchir
Marketplace and Quality Assurance Presentation - Vincent Chirchirictsugar
 
APRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdfAPRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdfRbc Rbcua
 
8447779800, Low rate Call girls in Tughlakabad Delhi NCR
8447779800, Low rate Call girls in Tughlakabad Delhi NCR8447779800, Low rate Call girls in Tughlakabad Delhi NCR
8447779800, Low rate Call girls in Tughlakabad Delhi NCRashishs7044
 
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...lizamodels9
 
Investment in The Coconut Industry by Nancy Cheruiyot
Investment in The Coconut Industry by Nancy CheruiyotInvestment in The Coconut Industry by Nancy Cheruiyot
Investment in The Coconut Industry by Nancy Cheruiyotictsugar
 
Kenya Coconut Production Presentation by Dr. Lalith Perera
Kenya Coconut Production Presentation by Dr. Lalith PereraKenya Coconut Production Presentation by Dr. Lalith Perera
Kenya Coconut Production Presentation by Dr. Lalith Pereraictsugar
 
Innovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdfInnovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdfrichard876048
 
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,noida100girls
 
8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR
8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR
8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCRashishs7044
 
Market Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 EditionMarket Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 EditionMintel Group
 
Youth Involvement in an Innovative Coconut Value Chain by Mwalimu Menza
Youth Involvement in an Innovative Coconut Value Chain by Mwalimu MenzaYouth Involvement in an Innovative Coconut Value Chain by Mwalimu Menza
Youth Involvement in an Innovative Coconut Value Chain by Mwalimu Menzaictsugar
 
Digital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdfDigital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdfJos Voskuil
 
Call US-88OO1O2216 Call Girls In Mahipalpur Female Escort Service
Call US-88OO1O2216 Call Girls In Mahipalpur Female Escort ServiceCall US-88OO1O2216 Call Girls In Mahipalpur Female Escort Service
Call US-88OO1O2216 Call Girls In Mahipalpur Female Escort Servicecallgirls2057
 
Kenya’s Coconut Value Chain by Gatsby Africa
Kenya’s Coconut Value Chain by Gatsby AfricaKenya’s Coconut Value Chain by Gatsby Africa
Kenya’s Coconut Value Chain by Gatsby Africaictsugar
 
Independent Call Girls Andheri Nightlaila 9967584737
Independent Call Girls Andheri Nightlaila 9967584737Independent Call Girls Andheri Nightlaila 9967584737
Independent Call Girls Andheri Nightlaila 9967584737Riya Pathan
 
Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...
Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...
Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...lizamodels9
 
Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...Seta Wicaksana
 
8447779800, Low rate Call girls in Uttam Nagar Delhi NCR
8447779800, Low rate Call girls in Uttam Nagar Delhi NCR8447779800, Low rate Call girls in Uttam Nagar Delhi NCR
8447779800, Low rate Call girls in Uttam Nagar Delhi NCRashishs7044
 

Dernier (20)

Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deckPitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
Pitch Deck Teardown: Geodesic.Life's $500k Pre-seed deck
 
Marketplace and Quality Assurance Presentation - Vincent Chirchir
Marketplace and Quality Assurance Presentation - Vincent ChirchirMarketplace and Quality Assurance Presentation - Vincent Chirchir
Marketplace and Quality Assurance Presentation - Vincent Chirchir
 
APRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdfAPRIL2024_UKRAINE_xml_0000000000000 .pdf
APRIL2024_UKRAINE_xml_0000000000000 .pdf
 
8447779800, Low rate Call girls in Tughlakabad Delhi NCR
8447779800, Low rate Call girls in Tughlakabad Delhi NCR8447779800, Low rate Call girls in Tughlakabad Delhi NCR
8447779800, Low rate Call girls in Tughlakabad Delhi NCR
 
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
Call Girls In Sikandarpur Gurgaon ❤️8860477959_Russian 100% Genuine Escorts I...
 
Investment in The Coconut Industry by Nancy Cheruiyot
Investment in The Coconut Industry by Nancy CheruiyotInvestment in The Coconut Industry by Nancy Cheruiyot
Investment in The Coconut Industry by Nancy Cheruiyot
 
Kenya Coconut Production Presentation by Dr. Lalith Perera
Kenya Coconut Production Presentation by Dr. Lalith PereraKenya Coconut Production Presentation by Dr. Lalith Perera
Kenya Coconut Production Presentation by Dr. Lalith Perera
 
Innovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdfInnovation Conference 5th March 2024.pdf
Innovation Conference 5th March 2024.pdf
 
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
BEST Call Girls In Greater Noida ✨ 9773824855 ✨ Escorts Service In Delhi Ncr,
 
8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR
8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR
8447779800, Low rate Call girls in Kotla Mubarakpur Delhi NCR
 
Market Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 EditionMarket Sizes Sample Report - 2024 Edition
Market Sizes Sample Report - 2024 Edition
 
Japan IT Week 2024 Brochure by 47Billion (English)
Japan IT Week 2024 Brochure by 47Billion (English)Japan IT Week 2024 Brochure by 47Billion (English)
Japan IT Week 2024 Brochure by 47Billion (English)
 
Youth Involvement in an Innovative Coconut Value Chain by Mwalimu Menza
Youth Involvement in an Innovative Coconut Value Chain by Mwalimu MenzaYouth Involvement in an Innovative Coconut Value Chain by Mwalimu Menza
Youth Involvement in an Innovative Coconut Value Chain by Mwalimu Menza
 
Digital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdfDigital Transformation in the PLM domain - distrib.pdf
Digital Transformation in the PLM domain - distrib.pdf
 
Call US-88OO1O2216 Call Girls In Mahipalpur Female Escort Service
Call US-88OO1O2216 Call Girls In Mahipalpur Female Escort ServiceCall US-88OO1O2216 Call Girls In Mahipalpur Female Escort Service
Call US-88OO1O2216 Call Girls In Mahipalpur Female Escort Service
 
Kenya’s Coconut Value Chain by Gatsby Africa
Kenya’s Coconut Value Chain by Gatsby AfricaKenya’s Coconut Value Chain by Gatsby Africa
Kenya’s Coconut Value Chain by Gatsby Africa
 
Independent Call Girls Andheri Nightlaila 9967584737
Independent Call Girls Andheri Nightlaila 9967584737Independent Call Girls Andheri Nightlaila 9967584737
Independent Call Girls Andheri Nightlaila 9967584737
 
Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...
Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...
Call Girls In Radisson Blu Hotel New Delhi Paschim Vihar ❤️8860477959 Escorts...
 
Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...Ten Organizational Design Models to align structure and operations to busines...
Ten Organizational Design Models to align structure and operations to busines...
 
8447779800, Low rate Call girls in Uttam Nagar Delhi NCR
8447779800, Low rate Call girls in Uttam Nagar Delhi NCR8447779800, Low rate Call girls in Uttam Nagar Delhi NCR
8447779800, Low rate Call girls in Uttam Nagar Delhi NCR
 

Don T Mock Yourself Out Presentation

  • 1. Don’t Mock Yourself Out David Chelimsky Articulated Man, Inc
  • 2.
  • 7. classicist mockist
  • 8. merbist railsist
  • 10.
  • 11. ist bin ein red herring
  • 12. The big issue here is when to use a mock http://martinfowler.com/articles/mocksArentStubs.html
  • 13. agenda ๏ overview of stubs and mocks ๏ mocks/stubs applied to rails ๏ guidelines and pitfalls ๏ questions
  • 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. 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. 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
  • 18. 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
  • 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. 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. 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. 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
  • 23. 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
  • 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. 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. 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. 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
  • 29. 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
  • 30. 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
  • 31. things aren’t always as they seem
  • 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. 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. 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
  • 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 class Statement def header quot;Statement for #{@customer.name}quot; end end
  • 36. 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
  • 37. 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
  • 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. 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. 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. 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
  • 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 ???? class Statement def header quot;Statement for #{@customer.name}quot; end end
  • 43. 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
  • 44. 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
  • 45. stubs are often used like mocks
  • 46. mocks are often used like stubs
  • 47. we verify stubs by checking state after an action
  • 48. we tell mocks to verify interactions
  • 49. sometimes stubs just make the system run
  • 53. 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
  • 54. 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
  • 56. network access Database Interface Database Subject Network Internets Interface
  • 57. 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
  • 58. network access Stub Database Code Subject Example Stub Network
  • 59. 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
  • 61. 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
  • 62. 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
  • 64. 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
  • 65. 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
  • 66. 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
  • 68. specifying/testing individual objects in isolation
  • 69. good fit with lots of little objects
  • 70.
  • 71. all of these concepts apply to the non-rails specific parts of our rails apps
  • 72. isolation testing the rails-specific parts of our applicationss
  • 73.
  • 74. V M C
  • 76. Browser Router View Controller Model Database
  • 77. rails testing ๏ unit tests ๏ functional tests ๏ integration tests
  • 78. rails unit tests ๏ model classes (repositories) ๏ model objects ๏ database
  • 79. rails functional tests ๏ model classes (repositories) ๏ model objects ๏ database ๏ views ๏ controllers
  • 80. rails functional tests ๏ model classes (repositories) ๏ model objects ๏ database ๏ views ๏ controllers
  • 81. rails functional tests ๏ model classes (repositories) ๏ model objects !DRY ๏ database ๏ views ๏ controllers
  • 82. rails integration tests ๏ model classes (repositories) ๏ model objects ๏ database ๏ views ๏ controllers ๏ routing/sessions
  • 83. rails integration tests ๏ model classes (repositories) ๏ model objects ๏ database !DRY ๏ views ๏ controllers ๏ routing/sessions
  • 87.
  • 88. rails integration tests + webrat shoulda, context, micronaut, etc
  • 89. customer specs are implemented as end to end tests
  • 90. developer specs are implemented as isolation tests
  • 91. mocking and stubbing with rails
  • 92. 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
  • 93. 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
  • 94. 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
  • 95. 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
  • 96. shave a few lines but leave a little stubble http://github.com/dchelimsky/stubble
  • 97. 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
  • 98. 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
  • 99. 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
  • 100. 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
  • 101. guidlines, pitfalls, and common concerns
  • 102. focus on roles Mock Roles, not Objects http://www.jmock.org/oopsla2004.pdf
  • 105. complex setup is a red flag for design issues
  • 106. don’t stub/mock the object you’re testing
  • 108. :refactoring => <<-DEFINITION Improving design without changing behaviour DEFINITION
  • 110.
  • 111. 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
  • 112. 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
  • 113. 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
  • 114. 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
  • 115. 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
  • 116. 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
  • 118.
  • 119. 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/
  • 120. http://blog.davidchelimsky.net/ http://www.articulatedman.com/ http://rspec.info/ http://cukes.info/ http://pragprog.com/titles/achbd/the-rspec-book