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