RSpec允许/期望vs期望/和_return

在RSpec中,特别是版本> = 3,之间有什么区别:

  • 使用allow来设置返回测试双精度的参数的消息期望,然后使用expect对返回的测试双精度进行断言
  • 只需使用expect来设置参数的期望并返回测试double

或者只是语义学? 我知道提供/指定带有expect的返回值是RSpec模拟2.13中的语法 ,但据我所知,RSpec模拟3中的语法改变为使用allow

但是,在下面的(传递)示例代码中,使用allow / expect或者只是expect / and_return似乎会生成相同的结果。 如果一种语法比另一种更受欢迎,也许我会期望有某种弃用通知,但由于没有,似乎两种语法都被认为是有效的:

 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 

如果我故意通过将期望的传入baz参数更改为不同的测试double来使测试失败,则错误几乎相同:

  1) SomethingThatCallsFoo#some_long_process using allow/expect calls quux method on result of Foo.bar(baz) Failure/Error: Foo.bar(baz).qux  received :bar with unexpected arguments expected: (#) got: (#) 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 ' 2) SomethingThatCallsFoo#some_long_process using expect/and_return calls quux method on result of Foo.bar(baz) Failure/Error: Foo.bar(baz).qux  received :bar with unexpected arguments expected: (#) got: (#) # ./foo_test.rb:16:in `some_long_process' # ./foo_test.rb:43:in `block (4 levels) in ' 

那么,这两个测试之间是否有任何真正的差异,无论是结果还是表达意图,还是只是语义和/或个人偏好? 应该allow / expect通常使用expect / and_return ,因为它似乎是替换语法,或者它们中的每一个是否都用于特定的测试场景?

更新

在阅读了Mori的答案之后,我从上面的示例代码中注释掉了Foo.bar(baz).qux行,并得到以下错误:

  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 ' 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) ().bar(#) expected: 1 time with arguments: (#) received: 0 times # ./foo_test.rb:41:in `block (4 levels) in ' 
  • allow规范失败,因为foobar_result double永远不会代表Foo.bar(baz)的结果,因此从来没有#qux调用它
  • expect规格在Foo永远不会收到.bar(baz)失败,所以我们甚至没有到达审讯foobar_result double的程度

有道理:它不仅仅是语法更改,而且expect / and_return确实具有与allow / expect不同的目的。 我真的应该检查一下最明显的地方: RSpec Mocks README ,特别是以下几节:

  • 模拟对象和测试存根
  • 特定于测试的扩展
  • 设置回复

请参阅经典文章Mocks Are Not Stubs 。 allow使用存根而expect进行模拟。 那就是allow一个对象返回X而不是它将返回的任何东西,并且expect是一个allow 加上一些状态或事件的期望。 当你写作

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

…你告诉规范环境修改Foo以在收到时返回foobar_result :bar with baz 。 但是当你写作

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

…你正在做同样的事情,加上告诉规范失败, 除非 Foo收到:barbaz

要查看差异,请在Foo 收到的示例中尝试:bar with baz