In RSpec, specifically version >= 3, is there any difference between:
- 使用
allow
设置消息期望值,参数返回test double,然后使用expect
对返回的test double进行断言 - 只需使用
expect
设置带有参数的期望值,并返回测试结果
还是仅仅是语义学?我知道用expect
提供/指定返回值是the syntax in RSpec mocks 2.13,但据我所知,用the syntax changed in 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
如果我故意将Expection中的passed in 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
之上使用,因为它似乎是替换语法,还是它们都应该在特定的测试场景中使用?
Update
在阅读了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_return
的目的与allow
/expect
不同.我真的应该判断最明显的地方:RSpec Mocks README,特别是以下部分: