Using an object double
object_double
can be used to create a double from an existing "template" object, from
which it verifies that any stubbed methods on the double also exist on the template. This is
useful for objects that are readily constructable, but may have far-reaching side-effects
such as talking to a database or external API. In this case, using a double rather than the
real thing allows you to focus on the communication patterns of the object's interface
without having to worry about accidentally causing side-effects. Object doubles can also be
used to verify methods defined on an object using method_missing
, which is not possible
with instance_double
.
In addition, object_double
can be used with specific constant values, as shown below. This
is for niche situations, such as when dealing with singleton objects.
Doubling an existing object
Given a file named "spec/user_spec.rb" with:
class User
# Don't want to accidentally trigger this!
def save; sleep 100; end
end
def save_user(user)
"saved!" if user.save
end
RSpec.describe '#save_user' do
it 'renders message on success' do
user = object_double(User.new, :save => true)
expect(save_user(user)).to eq("saved!")
end
end
When I run rspec spec/user_spec.rb
Then the examples should all pass.
Doubling a constant object
Given a file named "spec/email_spec.rb" with:
require 'logger'
module MyApp
LOGGER = Logger.new("myapp")
end
class Email
def self.send_to(recipient)
MyApp::LOGGER.info("Sent to #{recipient}")
# other emailing logic
end
end
RSpec.describe Email do
it 'logs a message when sending' do
logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const
Email.send_to('hello@foo.com')
expect(logger).to have_received(:info).with("Sent to hello@foo.com")
end
end
When I run rspec spec/email_spec.rb
Then the examples should all pass.