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

z31licg0  于 2023-01-25  发布在  Ruby
关注(0)|答案(1)|浏览(251)

在RSpec中,特别是版本〉= 3,以下各项之间是否存在差异:

  • 使用allow设置消息期望值,参数返回测试双精度值,然后使用expect对返回的测试双精度值进行Assert
  • 只需使用expect设置带参数的期望值并返回测试双精度浮点数

我知道用expect提供/指定返回值是RSpec mocks 2.13中的语法,但据我所知,语法在RSpec mocks 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

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

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

那么,这两个测试之间是否存在真正的差异,无论是结果还是表达的意图,或者只是语义和/或个人偏好?一般来说,allow/expect应该比expect/and_return更好地使用,因为它看起来像是替换语法,还是它们都应该在特定的测试场景中使用?

    • 更新**

在阅读了Mori's answer之后,我注解掉了上面示例代码中的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)>'
  • allow规范失败,因为foobar_result double永远无法替代Foo.bar(baz)的结果,因此永远不会调用#qux
  • expect规范在Foo从未接收.bar(baz)的点处失败,因此我们甚至没有到达询问foobar_result双精度浮点数的点

有道理:这不仅仅是语法更改,而且expect/and_returnallow/expect确实有不同用途。RSpec Mocks README,特别是以下部分:

  • 模拟对象和测试存根
  • 测试特定扩展
  • 设置响应
tvz2xvvm

tvz2xvvm1#

请参阅经典文章Mocks Aren't Stubsallow生成存根而expect生成模拟。即allow允许对象返回X而不是返回未存根的任何内容,而expectallow * 加上 * 某个状态或事件的预期。

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

...您告诉规范环境修改Foo,以便在它收到带有baz:bar时返回foobar_result

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

...您正在执行相同的操作,并告诉规范失败 *,除非 * Foo收到:barbaz
要查看两者的区别,请在Foo * 不 * 接收:barbaz的示例中尝试两者。

相关问题