Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dchelimsky/716800 to your computer and use it in GitHub Desktop.
Save dchelimsky/716800 to your computer and use it in GitHub Desktop.
#RSpec Mocks...
it "does something with a collaborator" do
#Given
collab = mock(:collab)
it.collaborator = collab
#Then
collab.should_receive(:call).with(3)
#When
it.do_something
end
#Spies using RR...
it "does something with a collaborator" do
#Given
collab = Object.new
stub(collab).call
# You say that rspec-mocks (and flexmock and mocha) suck because they force you to do things
# in a different order from the one and only order that you find to be valid, but the fact that this
# stub ^^ is necessary to make have_received (below) work adds the
# need for an extra line, binds these two lines together, and generally makes
# this example more difficult to understand than the one above. And this is a simple example.
# It's all trade-offs. -dchelimsky
# You have a very valid point re. stub(collab).call up there. I think it stinks too. I'd much prefer
# a dumb null object which collects all messages sent to it, responds with stubbed answers where asked to,
# and can then be asked what it received later.
# Nonetheless I *still* prefer stub(collab).call; #interact with collab; collab.should have_received, not
# just because of the order it's written in, but because I can assert that some collaborations happened
# without having to stub out all the other collaborations which don't interest me. -morticed
# Which you can do with collab.as_null_object, as noted below (and your pref for
# the default noted as well).
#
# Another problem with spies, however, is that when you care about return values,
# you _have_ to stub them in advance. Even if we made as_null_object the default
# we'd still end up with some examples that require stub statements before the
# action coupled with have_received statements after.
#
# As for making as_null_object the default, I don't have a strong philosophical
# position for or against it, but making that change now would lead to false
# positives in existing suites when they get upgraded, so it's not something I'd
# change without an overwhelmingly compelling reason to do so.
#
# What we _could_ do is add another generator method that creates a double that
# behaves like this. Not sure what to call it. Maybe null_object, but I'm not
# sure. If this interests you, please file a feature request at
# http://github.com/rspec/rspec-mocks/issues. -dchelimsky
it.collaborator = collab
#When
it.do_something
#Then
collab.should have_received.call(3)
end
#object code:
def my_awesome_method
logger.debug("Doing stuff with #{@collaborator.inspect}")
@collaborator.do_stuff
end
#RSpec Mocks:
it "should do stuff with collaborator" do
collaborator = mock(:collaborator_is_a_weird_looking_word)
# Change ^^ to mock(:collaborator_is_a_weird_looking_word).as_null_object and you don't
# need the next line. -dchelimsky
# Yes indeed, it's just that I think it should be the default. :) -morticed
collaborator.stub!(:inspect)
collaborator.should_receive(:do_stuff)
object.collaborator = collaborator
object.my_awesome_method
end
#Spies using RR
it "should do stuff with collaborator" do
collaborator = Object.new
stub(collaborator).do_stuff
object.collaborator = collaborator
object.my_awesome_method
collaborator.should have_received.do_stuff
end
#And then... what happens to the tests when we remove the debugging code?
def my_awesome_method
@collaborator.do_stuff
end

Seriously guys, just go get RR and then do this:

Spec::Runner.configure do |config|
  config.mock_with :rr
  # or if that doesn't work due to a version incompatibility
  # config.mock_with RR::Adapters::Rspec
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment