2015-01-18 14 views
22

In RSpec, in particolare la versione> = 3, v'è alcuna differenza tra:RSpec consentire/aspettarsi vs solo aspettare/and_return

  • Utilizzando allow per impostare le aspettative dei messaggi con parametri che restituiscono test raddoppia, e quindi utilizzando expect per fare un'affermazione sul test restituita raddoppia
  • usando solo expect per impostare l'aspettativa con i parametri e restituire il test doppia

o è tutto solo la semantica? So che fornire/specificare un valore di ritorno con expect era the syntax in RSpec mocks 2.13, ma per quanto posso vedere, the syntax changed in RSpec mocks 3 per utilizzare allow.

Tuttavia, nel codice (passaggio) riportato di seguito, utilizzando allow/expect o semplicemente expect/and_return sembra generare lo stesso risultato. Se uno sintassi è stata favorita rispetto ad un altro, forse, mi sarei aspettato che ci sia un qualche tipo di preavviso disapprovazione, ma dal momento che non v'è, sembrerebbe che entrambe le sintassi sono considerati validi:

class Foo 
    def self.bar(baz) 
    # not important what happens to baz parameter 
    # only important that it is passed in 
    new 
    end 

    def qux 
    # perform some action 
    end 
end 

class SomethingThatCallsFoo 
    def some_long_process(baz) 
    # do some processing 
    Foo.bar(baz).qux 
    # do other processing 
    end 
end 

describe SomethingThatCallsFoo do 
    let(:foo_caller) { SomethingThatCallsFoo.new } 

    describe '#some_long_process' do 
    let(:foobar_result) { double('foobar_result') } 
    let(:baz) { double('baz') } 

    context 'using allow/expect' do 
     before do 
     allow(Foo).to receive(:bar).with(baz).and_return(foobar_result) 
     end 

     it 'calls qux method on result of Foo.bar(baz)' do 
     expect(foobar_result).to receive(:qux) 
     foo_caller.some_long_process(baz) 
     end 
    end 

    context 'using expect/and_return' do 
     it 'calls qux method on result of Foo.bar(baz)' do 
     expect(Foo).to receive(:bar).with(baz).and_return(foobar_result) 
     expect(foobar_result).to receive(:qux) 
     foo_caller.some_long_process(baz) 
     end 
    end 
    end 
end 

Se ho deliberatamente fare i test falliscono cambiando il passato-in baz parametro nella aspettativa di un diverso doppia prova, gli errori sono praticamente la stessa:

1) SomethingThatCallsFoo#some_long_process using allow/expect calls quux method on result of Foo.bar(baz) 
    Failure/Error: Foo.bar(baz).qux 
     <Foo (class)> received :bar with unexpected arguments 
     expected: (#<RSpec::Mocks::Double:0x3fe97a0127fc @name="baz">) 
       got: (#<RSpec::Mocks::Double:0x3fe97998540c @name=nil>) 
     Please stub a default value first if message might be received with other args as well. 
    # ./foo_test.rb:16:in `some_long_process' 
    # ./foo_test.rb:35:in `block (4 levels) in <top (required)>' 

    2) SomethingThatCallsFoo#some_long_process using expect/and_return calls quux method on result of Foo.bar(baz) 
    Failure/Error: Foo.bar(baz).qux 
     <Foo (class)> received :bar with unexpected arguments 
     expected: (#<RSpec::Mocks::Double:0x3fe979935fd8 @name="baz">) 
       got: (#<RSpec::Mocks::Double:0x3fe979cc5c0c @name=nil>) 
    # ./foo_test.rb:16:in `some_long_process' 
    # ./foo_test.rb:43:in `block (4 levels) in <top (required)>' 

Quindi, ci sono differenze reali tra questi due test, sia nel risultato o intento espresso, o è solo semantica e/o preferenza personale? Dovrebbe essere utilizzato allow/expect su expect/and_return in generale in quanto sembra che sia la sintassi di sostituzione, o ognuno di essi è destinato ad essere utilizzato in scenari di test specifici?

Aggiornamento

Dopo aver letto Mori's answer 's, ho commentato la linea Foo.bar(baz).qux dal codice esempio di cui sopra, ed ha ottenuto i seguenti errori:

1) SomethingThatCallsFoo#some_long_process using allow/expect calls qux method on result of Foo.bar(baz) 
    Failure/Error: expect(foobar_result).to receive(:qux) 
     (Double "foobar_result").qux(any args) 
      expected: 1 time with any arguments 
      received: 0 times with any arguments 
    # ./foo_test.rb:34:in `block (4 levels) in <top (required)>' 

    2) SomethingThatCallsFoo#some_long_process using expect/and_return calls qux method on result of Foo.bar(baz) 
    Failure/Error: expect(Foo).to receive(:bar).with(baz).and_return(foobar_result) 
     (<Foo (class)>).bar(#<RSpec::Mocks::Double:0x3fc211944fa4 @name="baz">) 
      expected: 1 time with arguments: (#<RSpec::Mocks::Double:0x3fc211944fa4 @name="baz">) 
      received: 0 times 
    # ./foo_test.rb:41:in `block (4 levels) in <top (required)>' 
  • Il allow spec non riesce perché la foobar_result double non riesce a stare in piedi per il risultato di Foo.bar(baz), e quindi non ha mai chiamato #qux
  • Il expect spec non riesce al punto di Foo mai ricevere .bar(baz) quindi non abbiamo nemmeno arriviamo al punto di interrogare il foobar_result doppia

un senso: non è solo un cambiamento di sintassi, e che expect/and_return ha uno scopo diverso da allow/expect.Ho davvero dovuto controllare il luogo più ovvio: il RSpec Mocks README, in particolare, le seguenti sezioni:

risposta

58

Vedi il classico articolo Mocks Aren't Stubs. allow fa uno stub mentre expect fa una finta. Questo è allow consente a un oggetto di restituire X invece di qualsiasi cosa restituisca non interrotto, e expect è un allowpiù un'aspettativa di qualche stato o evento. Quando si scrive

allow(Foo).to receive(:bar).with(baz).and_return(foobar_result) 

... che stai dicendo l'ambiente specifiche per modificare Foo per tornare foobar_result quando riceve :bar con baz. Ma quando si scrive

expect(Foo).to receive(:bar).with(baz).and_return(foobar_result) 

... si sta facendo lo stesso, oltre a raccontare le specifiche a fallire a meno che Foo riceve :bar con baz.

Per vedere la differenza, provare entrambi in casi in cui non Foonon ricevono :bar con baz.