rspec: How to stub an instance method called by constructor?
Question
class A
def initialize
@x = do_something
end
def do_something
42
end
end
How can I stub do_something
in rspec, before the original implementation is called (thus assigning 42 to @x
)? And without changing the implementation, of course.
Solution
Here's the commit which adds the feature to rspec - This was on May 25 2008. With this you can do
A.any_instance.stub(do_something: 23)
However, the latest gem version of rspec (1.1.11, October 2008) doesn't have this patch in it.
This ticket states that they yanked it out for maintenance reasons, and an alternative solution hasn't yet been provided.
Doesn't look like you can do it at this point. You'll have to hack the class manually using alias_method or somesuch.
OTHER TIPS
I've found this solution on http://pivotallabs.com/introducing-rr/
new_method = A.method(:new)
A.stub!(:new).and_return do |*args|
a = new_method.call(*args)
a.should_receive(:do_something).and_return(23)
a
end
I don't know how to do that in spec's mock framework, but you can easily swap it out for mocha to do the following:
# should probably be in spec/spec_helper.rb
Spec::Runner.configure do |config|
config.mock_with :mocha
end
describe A, " when initialized" do
it "should set x to 42" do
A.new.x.should == 42
end
end
describe A, " when do_something is mocked" do
it "should set x to 23" do
A.any_instance.expects(:do_something).returns(23)
A.new.x.should == 23
end
end
or with RR:
stub.any_instance_of(A).do_something { 23 }
In the latest version of RSpec as of today - 3.5 you can:
allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")
I do like Denis Barushev answer. And I'd like to suggest only one cosmetic change which makes new_method
variable unnecessary. Rspec does munge on stubbed methods, so that they can be accessed with proxied_by_rspec__
prefix:
A.stub!(:new).and_return do |*args|
a = A.proxied_by_rspec__new(*args)
a.should_receive(:do_something).and_return(23)
a
end
In RSpec 2.6 or later you can use
A.any_instance.stub(do_something: 23)
See the docs for more details. (Thanks to rogerdpack for pointing out that this is now possible - I thought it deserved an answer of its own)
To stub an instance method, you can do something like this:
before :each do
@my_stub = stub("A")
@my_stub.should_receive(:do_something).with(no_args()).and_return(42)
@my_stub.should_receive(:do_something_else).with(any_args()).and_return(true)
A.stub(:new).and_return(my_stub)
end
But as pschneider pointed out, just return 42 on new with:
A.stub(:new).and_return(42)
or something to that effect.
Here is an idea which might not be very elegant but is basically sure to work:
Create a tiny class that inherits the class you want to test, override the initialize method and call super
after having created the stubs in the initialize, like so:
it "should call during_init in initialize" do
class TestClass < TheClassToTest
def initialize
should_receive(:during_init)
super
end
end
TestClass.new
end
And there you go! I just used this successfully in one of my tests.
The rspec_candy gem comes with a stub_any_instance
helper method that works in both RSpec 1 and RSpec 2.
In my version of rspec (1.2.2) I can do this:
A.should_receive(:new).and_return(42)
I know it is probably to late to answer to the original poster, but I answer it anyway for future reference, as I came here with the same question but figure it out it was working on the latest rspec version.