【问题标题】:RSpec simulating calling the function argumentRSpec 模拟调用函数参数
【发布时间】:2017-04-27 11:24:55
【问题描述】:

如何模拟调用我正在使用 RSpec 测试的函数参数 我是这样做的:

module Module1
  def find_item(str, item_class)
    arr = item_class.find_or_initialize_by(...)
  end
end

然后在规格中:

let!(:dummy_class) { Class.new { extend Module1 } }

it 'calls find_or_initialize_by' do
  item_class = double("Item")
  allow(dummy_class).to receive(:item_class).and_return(item_class)
  expect(item_class).to receive(:find_or_initialize_by)
  dummy_class.find_item("item1", Item)
end

它会引发错误“# 未实现:item_class” 我试图使用 class_double 和 instance_double 但没有用。

失败:

1) 模块 1#find_items 失败/错误:allow(dummy_class).to receive(:item_class) # 未实现:item_class # ./spec/..._spec.rb:26:in `block (3 levels) in '

26行:allow(dummy_class).to receive(:item_class).and_return(item_class)

【问题讨论】:

标签: ruby-on-rails ruby ruby-on-rails-4 rspec mocking


【解决方案1】:

不知道你为什么要这样做,但你离得太远了。

目前你正在尝试使用

allow(dummy_class).to receive(:item_class).and_return(item_class)

item_class 不是一个方法,它只是一个local_variable

Message Allowance 简而言之语法是allow(object).to receive(method_name).with(arguments).and_return(return_value)

Message Expectation 语法是expect(object).to receive(method_name).with(arguments).and_return(return_value)

所以也许你的意思是allow(dummy_class).to receive(:find_item).with(item_class).and_return(item_class),因为find_item 是实际被调用的方法,item_class 是传入的参数,但是由于你是存根返回值,所以方法的主体永远不会发生。

你还可以allow(dummy_class).to recieve(:find_item).and_call_original 更多,但这并没有真正的目的,因为dummy_class 不是双重的,并且已经“允许”调用其原始版本的find_item

所以让我们使用dummy_class 的本机功能和allow(item_class).to receive(:find_or_initialize_by),然后以下将起作用。

it 'calls find_or_initialize_by' do
  item_class = double("Item")
  allow(item_class).to recieve(:find_or_initialize_by) #needed because it is a test double and knows nothing
  expect(item_class).to receive(:find_or_initialize_by) 
  dummy_class.find_item("item1", item_class) #used test double here to trap messages
end

或者,我们可以使用 Item 的部分双精度并跳过 Test Double item_class 例如

#please note this binds the test to Item existing
it 'calls find_or_initialize_by' do
  allow(Item).to receive(:find_or_initialize_by) #now a stubbed method on a partial double(Item)
  expect(Item).to receive(:find_or_initialize_by) 
  dummy_class.find_item("item1", Item) #used the partial double
end

Partial doubles 很好,因为它们可以验证双精度,并确保Object 在存根之前实际定义了该方法。

鉴于您的测试的性质以及dummy_class 不是双重的事实(因此不需要allow 任何东西)并且您没有测试任何返回值只是我建议使用的调用Spy 因为它们仅用于消息期望。 这使得测试更简单、更清晰并且没有任何依赖:

it 'calls find_or_initialize_by' do
  item = spy("item")
  dummy_class.find_item("item1", item)
  expect(item).to have_received(:find_or_initialize_by)
end   

它们也有 Partial Double 风格,但这取决于 Item 是一个已知且已加载的 Object:(与上述非常相似,但期望是调用的后遗症)

#please note this binds the test to Item existing
it 'calls find_or_initialize_by' do
  allow(Item).to receive(:find_or_initialize_by)
  dummy_class.find_item("item1", Item)
  expect(Item).to have_received(:find_or_initialize_by)
end 

另外让我们假设Module1 中的(...) 看起来像name: str,那么我建议测试它是否也使用正确的参数调用,例如

it 'calls find_or_initialize_by with args' do
  item = spy("item")
  dummy_class.find_item("item1", item)
  expect(item).to have_received(:find_or_initialize_by).with(name: 'item1')
end  

这可确保不仅进行了调用,而且将预期的参数传递给了调用。

尤其是Module 消息期望测试,我会尽量保持测试双打和间谍,因为它使您的测试独立且快速(当Item 不再存在时会发生什么?)

【讨论】:

    猜你喜欢
    • 2022-08-23
    • 1970-01-01
    • 2022-07-26
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 2021-11-27
    相关资源
    最近更新 更多