Issue stubbing with RSpec

rspec, ruby, stub

Solution

The "spy pattern" of RSpec requires that the objects have been previously stubbed. However, `any_instance.stub` doesn't actually stub the methods "for real" unless/until the method is invoked on a particular object. As such, the methods appears as being "unstubbed" and you get the error you're getting. Here's some code that demonstrates the change in definition:

class Foo
end

describe "" do
  it "" do
    Foo.any_instance.stub(:bar)
    foo1 = Foo.new
    foo2 = Foo.new
    print_bars = -> (context) {puts "#{context}, foo1#bar is #{foo1.method(:bar)}, foo2#bar is #{foo2.method(:bar)}"}
    print_bars['before call']
    foo1.bar
    print_bars['after call']
  end
end

which produces the following output:

before call, foo1#bar is #<Method: Foo#bar>, foo2#bar is #<Method: Foo#bar>
after call, foo1#bar is #<Method: #<Foo:0x007fc0c3842ef8>.bar>, foo2#bar is #<Method: Foo#bar>

I reported this an issue on RSpec's github site and got this acknowledgement/response.

You can use the following alternative approach, which depends on the recently introduced `expect_any_instance_of` method.

class Roll
  def initialize
    install if !installed?
  end
  def install; puts 'install'; end
end

describe Roll do
  before do
    class RollTestClass < Roll; end
  end

  let(:roll_class) { RollTestClass }
  let(:roll) { RollTestClass.new }

  context 'when installed is true' do
    before do
      roll_class.any_instance.stub(:installed?).and_return(true)
    end

    it 'should not call install' do
      expect_any_instance_of(roll_class).to_not receive(:install)
      roll
    end
  end

  context 'when installed is false' do
    before do
      roll_class.any_instance.stub(:installed?).and_return(false)
    end

    it 'should call install' do
      expect_any_instance_of(roll_class).to receive(:install)
      roll
    end
  end
end

Problem

I am trying to understand why the result of these tests, the first test claims the method is not stubbed, however, the 2nd one is. ``` class Roll def initialize install if !installed? end def install; puts 'install'; end end describe Roll do before do class RollTestClass < Roll; end RollTestClass.any_instance.stub(:install) end let(:roll_class) { RollTestClass } let(:roll) { RollTestClass.new } context 'when installed is true' do before do roll_class.any_instance.stub(:installed?).and_return(true) end it 'should not call install' do expect(roll).to_not have_received(:install) end end context 'when installed is false' do before do roll_class.any_instance.stub(:installed?).and_return(false) end it 'should call install' do expect(roll).to have_received(:install) end end end ``` It's also strange the error says `expected to have received install`, but I think that is likely just faulty feedback from the RSpec DSL. But maybe worth noting. ``` 1) Roll when installed is true should not call install Failure/Error: expect(roll).to_not have_received(:install) #<RollTestClass:0x10f69ef78> expected to have received install, but that method has not been stubbed. ```

Original source