Rspec,你能存根一个 object 上不存在的方法吗(或者模拟一个可以采用任何方法的 object )?

Rspec, can you stub a method that doesn't exist on an object (or mock an object that can take any method)?

这听起来像是一个非常基础的问题...所以让我解释一下上下文。我正在从 Stripe 检索事件,但出于此目的,但这适用于我考虑过的其他事情。

我在 Stripe object 中需要的数据被埋没了,所以我不得不做 event.data.object.date.lines.first.id。我不确定如何 mock/stub 这个...

我能想到的唯一方法是创建一个模拟 Stripe 事件的假模型 object(在我的这个例子中,请记住 Stripe 事件不能由用户创建)。然后我可以嘲笑那个假模型。但这真的是这样做的方法吗?有什么方法我可以只使用 Rspec 来存根整个 shebang 并说 event.data.object.date.lines.first.id 无论它出现在哪里都应该重新调整 1?

哦还有...如果没有,事实证明我必须创建一个假模型...非常感谢提示,因为现在我认为这个 "Fake" 模型需要很多层children,完全不值得投入时间。

RSpec 没有提供特殊的机制来访问被测元素,所以是的,你需要以某种方式 stub id 方法并让它 return 无论你希望(例如 1)。

要做到这一点,您必须有一种方法来访问测试中的 event 对象,以便您可以存根它的 data 方法。一旦可以访问它,就可以使用 RSpec 的 receive_message_chain 匹配器来指定方法序列,如果调用这些方法,您将 return 1.

如果有一些 public Strip 方法可以为事件模拟 return 一个 double,那将是最直接的方法。

如果那不可能,您可以确定 event 的 class 并使用 allow_any_instance_of 方法存根它的 data 方法。生成的模拟如下,假设 Foo 是您关注的 event 对象的 class:

allow_any_instance_of(Foo)).to receive_message_chain(
  :data, :object, :date, :lines, :first, :id).and_return(1)

这是一个通过的测试:

module Stripe
  class Event ; end
end
describe 'stack overflox example' do
  it 'should pass' do
    event = Stripe::Event.new
    expect(event).to receive_message_chain(:data, :object, :date, :lines, :first, :id).and_return(23)
    expect(event.data.object.date.lines.first.id).to eq(23)
  end
end

在 Stripe 的这种给定情况下,我会选择使用库进行模拟,而不是使用 RSpec 创建存根。我已经使用并强烈建议 stripe-ruby-mock.

这种方法的好处是您可以更接近地模拟 Stripe 的实际工作方式,并且您可以 switch to the actual stripe test api very easily