Rspec:期待协会收到消息
Rspec: expect an association to receive a message
我在 Rails 6 Rspec 3.8.0.
我有一个模型 A
,其中 belongs_to
B。我正在尝试用 A
编写一个单元测试作为 subject
:
expect(subject.b).to receive(:to_s)
subject.my_fn
然而这个规范总是失败,说 B
的实例没有收到消息,尽管我已经将 binding.pry
放在 运行 的实际代码中并验证了 a.b.to_s
被调用:
class A
def my_fn
binding.pry
b.to_s
end
end
我什至尝试过:
expect(a).to receive(:b).and_return(b)
expect(b).to receive(:to_s)
并且:
expect_any_instance_of(b.class).to receive(:to_s)
然而,对 to_s
的所有期望都落空了。这是为什么?
似乎你存根 b
关系更有意义。它看起来像:
expect(a).to receive(:b).and_return(stub(:b, to_s: 'foo_bar')
它没有显示在您的代码中,但我感觉您在设置 "receive" 期望之前调用了 code
。简单地说,代码执行应该如下所示:
it 'something' do
expect(subject.b).to receive(:to_s)
# write code here that would eventually call `a.b.to_s` (as you have said)
# i.e.
# `subject.some_method` (assuming `some_method` is your method that calls `a.b.to_s`
# don't call `subject.some_method` before the `expect` block above.
end
- 此外,以防万一您还不知道,请确保它是您传递给预期的同一对象实例:
expect(THE_ARG) ... receive()
和您正在测试的要调用的对象。如果它们具有相同的object_id
: ,您可以验证它们是否相同
it 'something' do
puts subject.b.object_id
# => 123456789
subject.some_method
end
# the class/method you're unit-testing:
class Foo
def some_method
# ...
puts b.object_id
# => 123456789
# ^ should also be the same
否则,如果它不是同一个对象(object_id 不匹配),您将不得不使用 expect_any_instance_of
(我只在最后使用它,因为它有潜在的危险,因为它期待 "any instance")... 或者你可以在你的规范文件中存根链 a.b.to_s
对象。
如果很难对整个链进行存根,但同时要避免使用 expect_any_instance_of
的陷阱,我会使用另一种变体来平衡便利性和规范准确性:
it 'something' do
expect_any_instance_of(subject.b.class).to receive(:to_s).once do |b|
expect(b.id).to eq(subject.b.id)
# the above just compares the `id` of the records (even if they are different objects in different memory-space)
# to demonstrate, say I do puts here:
puts b
# => #<SomeRecord:0x00005600e7a6f3b8 id:1 ...>
puts subject.b
# => #<SomeRecord:0x00005600e4f04138 id:1 ...>
puts b.id
# => 1
puts subject.b.id
# => 1
# notice that they are different objects (0x00005600e7a6f3b8 vs 0x00005600e4f04138)
# but the attribute id is the same (1 == 1)
end
subject.some_method
end
我在 Rails 6 Rspec 3.8.0.
我有一个模型 A
,其中 belongs_to
B。我正在尝试用 A
编写一个单元测试作为 subject
:
expect(subject.b).to receive(:to_s)
subject.my_fn
然而这个规范总是失败,说 B
的实例没有收到消息,尽管我已经将 binding.pry
放在 运行 的实际代码中并验证了 a.b.to_s
被调用:
class A
def my_fn
binding.pry
b.to_s
end
end
我什至尝试过:
expect(a).to receive(:b).and_return(b)
expect(b).to receive(:to_s)
并且:
expect_any_instance_of(b.class).to receive(:to_s)
然而,对 to_s
的所有期望都落空了。这是为什么?
似乎你存根 b
关系更有意义。它看起来像:
expect(a).to receive(:b).and_return(stub(:b, to_s: 'foo_bar')
它没有显示在您的代码中,但我感觉您在设置 "receive" 期望之前调用了 code
。简单地说,代码执行应该如下所示:
it 'something' do
expect(subject.b).to receive(:to_s)
# write code here that would eventually call `a.b.to_s` (as you have said)
# i.e.
# `subject.some_method` (assuming `some_method` is your method that calls `a.b.to_s`
# don't call `subject.some_method` before the `expect` block above.
end
- 此外,以防万一您还不知道,请确保它是您传递给预期的同一对象实例:
expect(THE_ARG) ... receive()
和您正在测试的要调用的对象。如果它们具有相同的object_id
: ,您可以验证它们是否相同
it 'something' do
puts subject.b.object_id
# => 123456789
subject.some_method
end
# the class/method you're unit-testing:
class Foo
def some_method
# ...
puts b.object_id
# => 123456789
# ^ should also be the same
否则,如果它不是同一个对象(object_id 不匹配),您将不得不使用 expect_any_instance_of
(我只在最后使用它,因为它有潜在的危险,因为它期待 "any instance")... 或者你可以在你的规范文件中存根链 a.b.to_s
对象。
如果很难对整个链进行存根,但同时要避免使用 expect_any_instance_of
的陷阱,我会使用另一种变体来平衡便利性和规范准确性:
it 'something' do
expect_any_instance_of(subject.b.class).to receive(:to_s).once do |b|
expect(b.id).to eq(subject.b.id)
# the above just compares the `id` of the records (even if they are different objects in different memory-space)
# to demonstrate, say I do puts here:
puts b
# => #<SomeRecord:0x00005600e7a6f3b8 id:1 ...>
puts subject.b
# => #<SomeRecord:0x00005600e4f04138 id:1 ...>
puts b.id
# => 1
puts subject.b.id
# => 1
# notice that they are different objects (0x00005600e7a6f3b8 vs 0x00005600e4f04138)
# but the attribute id is the same (1 == 1)
end
subject.some_method
end