Domanda

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.
È stato utile?

Soluzione

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top