用RSpec发出问题

时间:2013-11-22 19:00:08

标签: ruby rspec stub

我试图理解为什么这些测试的结果,第一个测试声称该方法没有存根,但是,第二个是。

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

错误说expected to have received install也很奇怪,但我认为这可能只是来自RSpec DSL的错误反馈。但也许值得注意。

  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.

1 个答案:

答案 0 :(得分:3)

RSpec的“间谍模式”要求对象先前已存根。但是,any_instance.stub实际上并不存储“for real”方法,除非/直到在特定对象上调用该方法。因此,这些方法显示为“未被取消”,您将收到错误。以下是一些演示定义更改的代码:

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

产生以下输出:

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>

我在RSpec的github网站上报告了这个问题并获得了this acknowledgement/response

可以使用以下替代方法,这取决于最近推出的expect_any_instance_of方法。

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