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.

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top