2015-01-18 12 views
22

W RSpec, szczególnie w wersji> = 3, jest jakaś różnica pomiędzy:RSpec pozwalają/oczekiwać vs tylko oczekiwać/and_return

  • Korzystanie allow założyć oczekiwania wiadomość z parametrami, które zwracają podwójne testowe, a następnie za pomocą expect aby twierdzenie na zwróconej testu podwaja
  • tylko przy użyciu expect skonfigurować oczekiwanie z parametrami i powrócić test podwójnego

czy to wszystko to tylko semantyka? Wiem, że dostarczanie/określanie wartości zwracanej z expect było the syntax in RSpec mocks 2.13, ale o ile widzę, the syntax changed in RSpec mocks 3 używać allow.

Jednakże kod (przejście) próbki poniżej, stosując albo allow/expect lub tylko expect/and_return zdaje się generowania ten sam rezultat. Jeśli jeden składnia była przedkładana nad inną, być może bym się spodziewać tam być jakiś rodzaj uprzedzenia amortyzacji, ale ponieważ nie ma, wydaje się, że obie składnie są uznawane za ważne:

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 

Gdybym celowo zrobić testy nie zmieniając przekazany w baz parametru w oczekiwaniu na inną testowym podwójne, błędy są niemal tak samo:

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)>' 

Więc, czy są jakieś istotne różnice między tymi dwoma badaniami, albo w wyniku lub wyrażone intencje, czy jest to tylko semantyka i/lub osobiste preferencje? Powinno się używać allow/ ponad expect/and_return ogólnie, ponieważ wydaje się, że jest to zastępcza składnia, lub czy każda z nich ma być używana w konkretnych scenariuszach testowych?

Aktualizacja

Po przeczytaniu Mori's answer „s, wypowiedziało się w prostej linii z przykładowym kodzie Foo.bar(baz).qux powyżej i dostał następujące błędy:

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)>' 
  • allow Spec nie działa, ponieważ foobar_result double nigdy nie stanie się na skutek Foo.bar(baz), a więc nigdy nie ma #qux nazwie go
  • expect specyfikacja nie w punkcie Foo nigdy otrzymaniu .bar(baz) więc nawet nie dostać się do punktu odpytywania foobar_result podwójne

ma sens: to nie tylko zmiana składni, a expect/and_return ma inny cel niż allow/expect.I naprawdę powinien mieć sprawdzone najbardziej oczywiste miejsce: RSpec Mocks README, a konkretnie w następujących sekcjach:

Odpowiedz

58

Patrz klasyczny artykuł Mocks Aren't Stubs. allow tworzy stub, a expect tworzy makiety. To jest allow pozwala obiektowi zwrócić X zamiast tego, co by go zwróciło, a expect jest oczekiwaniem jakiegoś stanu lub zdarzenia. Kiedy piszesz

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

... mówisz spec środowiska zmodyfikować Foo wrócić foobar_result gdy odbiera :bar z baz. Ale kiedy piszesz

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

... robisz to samo, plus mówi spec niepowodzenie chybaFoo odbiera :bar z baz.

Aby zobaczyć różnicę, spróbuj zarówno w przykładach gdzie Foo robi nie otrzymać :bar z baz.