ruby RSpec 允许/期望与仅期望/和返回

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28006913/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 06:40:22  来源:igfitidea点击:

RSpec allow/expect vs just expect/and_return

rubytestingrspecmockingrspec3

提问by Paul Fioravanti

In RSpec, specifically version >= 3, is there any difference between:

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

  • Using allowto set up message expectations with parameters that return test doubles, and then using expectto make an assertion on the returned test doubles
  • Just using expectto set up the expectation with parameters and return the test double
  • 使用allow返回测试替身的参数设置消息期望,然后使用expect对返回的测试替身进行断言
  • expect用于设置带参数的期望并返回测试替身

or is it all just semantics? I know that providing/specifying a return value with expectwas the syntax in RSpec mocks 2.13, but as far as I can see, the syntax changed in RSpec mocks 3to use allow.

或者这只是语义?我知道提供/指定返回值expectRSpec 模拟 2.13 中的语法,但据我所知,RSpec 模拟 3 中的语法更改为使用allow.

However, in the (passing) sample code below, using either allow/expector just expect/and_returnseems to generate the same result. If one syntax was favoured over another, perhaps I would have expected there to be some kind of deprecation notice, but since there isn't, it would seem that both syntaxes are considered valid:

但是,在下面的(传递)示例代码中,使用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

If I deliberately make the tests fail by changing the passed-in bazparameter in the expectation to a different test double, the errors are pretty much the same:

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

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

So, are there any real differences between these two tests, either in result or expressed intent, or is it just semantics and/or personal preference? Should allow/expectbe used over expect/and_returnin general as it seems like it's the replacement syntax, or are each of them meant to be used in specific test scenarios?

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

Update

更新

After reading Mori's answer's, I commented out the Foo.bar(baz).quxline from the example code above, and got the following errors:

阅读完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 <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)>'
  • The allowspec fails because the foobar_resultdouble never gets to stand in for the result of Foo.bar(baz), and hence never has #quxcalled on it
  • The expectspec fails at the point of Foonever receiving .bar(baz)so we don't even get to the point of interrogating the foobar_resultdouble
  • allow规范将失败,因为foobar_result双永远不会站在对的结果Foo.bar(baz),因此从来没有#qux要求它
  • expect规范在点失败Foo永远不会接收.bar(baz),所以我们甚至没有去询问点foobar_result

Makes sense: it's not just a syntax change, and that expect/and_returndoes have a purpose different to allow/expect. I really should have checked the most obvious place: the RSpec Mocks README, specifically the following sections:

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

回答by Mori

See the classic article Mocks Aren't Stubs. allowmakes a stub while expectmakes a mock. That is allowallows an object to return X instead of whatever it would return unstubbed, and expectis an allowplusan expectation of some state or event. When you write

请参阅经典文章Mocks Aren't Stubsallow制作存根,同时expect制作模拟。这是allow允许一个对象返回X,而不是不管它会返回unstubbed,并且expectallow加上一些状态或事件的预期。当你写

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

... you're telling the spec environment to modify Footo return foobar_resultwhen it receives :barwith baz. But when you write

...您是在告诉规范环境进行修改Foofoobar_result在它接收:barbaz. 但是当你写

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

... you're doing the same, plus telling the spec to fail unlessFooreceives :barwith baz.

...你做同样的,再加上告诉规范失效,除非Foo接收:barbaz

To see the difference, try both in examples where Foodoes notreceive :barwith baz.

要看到其中的差别,同时尝试在例子,其中Foo没有收到:barbaz