rspec: How to stub an instance method called by constructor?

喜夏-厌秋 提交于 2019-11-30 06:24:22

问题


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.


回答1:


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.




回答2:


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



回答3:


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



回答4:


or with RR:

stub.any_instance_of(A).do_something { 23 }



回答5:


In the latest version of RSpec as of today - 3.5 you can:

allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")



回答6:


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



回答7:


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)




回答8:


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.




回答9:


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.




回答10:


The rspec_candy gem comes with a stub_any_instance helper method that works in both RSpec 1 and RSpec 2.




回答11:


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.



来源:https://stackoverflow.com/questions/316294/rspec-how-to-stub-an-instance-method-called-by-constructor

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!