2013-11-22 39 views
1

我想了解爲什麼這些測試的結果,第一個測試聲稱方法不存在,但是,第二個是。用RSpec發出存根

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. 

回答

2

RSpec的「間諜模式」要求對象先前已被樁住。然而,any_instance.stub實際上並沒有將方法存儲爲「真實」,除非/直到在特定對象上調用該方法。因此,這些方法顯示爲「未打開」,並且您收到錯誤。下面是一些代碼,演示了在定義的變化:這會產生以下輸出

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 
+0

非常好!我決定發佈S.O.而不是在rspec-mocks github的問題,因爲我想我正在做一些非常規或不正確的事情。很高興知道它的rspec問題。 expect_any_instance_of策略有點尷尬,但它必須這樣做。謝謝! – brewster

+0

快速附錄:如果你使用Mocha進行模擬,當使用'any_instance'時,它將「實際」 – Neal