Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 什么时候你会在Rspec中使用间谍而不是双打?_Ruby_Rspec - Fatal编程技术网

Ruby 什么时候你会在Rspec中使用间谍而不是双打?

Ruby 什么时候你会在Rspec中使用间谍而不是双打?,ruby,rspec,Ruby,Rspec,我能理解测试双精度在Rspec中的应用。您可能需要测试一个方法,该方法有一个执行昂贵网络查询的对象。因此,您可以使用填充(test double)返回昂贵操作所需的值: class Contact def update @api_result = AmazonAPI.perform_expensive_task self.status = @api_result['status'] self.last_checked = @api_result['last_check

我能理解测试双精度在Rspec中的应用。您可能需要测试一个方法,该方法有一个执行昂贵网络查询的对象。因此,您可以使用填充(test double)返回昂贵操作所需的值:

class Contact
  def update
    @api_result = AmazonAPI.perform_expensive_task
    self.status = @api_result['status']
    self.last_checked = @api_result['last_checked']
    save!
  end
end

describe Contact do
  subject { Contact.new }

  describe '#update' do
    it "updates contact with api criteria" do 
      api = double('Amazon API')
      allow(api).to receive(:perform_expensive_task).and_return({ status: 1, last_checked: Time.now })
      subject.update
      expect(subject.status).to eq 1
    end
  end
end
我们需要测试update方法,但不想测试API查询。因此,我们使用一个测试双精度并将其存根,以解决需求

但后来我遇到了间谍。我看不出这有什么用。这是本教程中提供的示例:

let(:order) do
  spy('Order', process_line_items: nil, charge_credit_card: true, send_email: true)
end

before(:example) do
  order.process_line_items
  order.charge_credit_card
  order.send_email
end

it 'calls #process_line_items on the order' do
  expect(order).to have_received(:process_line_items)
end

it 'calls #charge_credit_card on the order' do
  expect(order).to have_received(:charge_credit_card)
end

it 'calls #send_email on the order' do
  expect(order).to have_received(:send_email)
end

这个特定的示例显式地调用了三个方法,稍后将检查是否调用了这三个方法。当然,它叫他们。它在测试中做对了。在实际情况下,我应该在什么时候使用spies?

Spice跟踪对其进行的呼叫(特别是它发送的消息)。因此,每当需要断言对合作者进行了特定调用时,您都会使用间谍

典型的用例是根据输入检查您的实现是否正在使用外部协作器。假设您打算有条件地记录日志,或者您可以检查作业是否使用特定参数排队,或者是否调用了某个mailer方法

间谍是保证你的目标正确合作的工具

更新

示例可以在@meta的答案中找到

带有代码的简单用例是logger one:

class SomeCommand
  def call(arg:, other:)
    if arg <= 0
      logger.warn("args should be positive")
    else
      logger.debug("all fine")
    end
    # more
  end

  def logger
    Rails.logger # for instance
  end
end

describe SomeCommand
  let(:logger) { spy('Logger') }

  # replace collaborator
  before { allow(subject).to receive(:logger) { logger } }

  context 'with negative value' do
    it 'warns' do
      subject.call(arg: -1, other: 6)
      expect(logger).to have_received(:warn)
      expect(logger).not_to have_received(:debug)
    end
  end

  context 'with positive value' do
    it 'logs as debug' do
      subject.call(arg: 1, other: 6)
      expect(logger).not_to have_received(:warn)
      expect(logger).to have_received(:debug)
    end
  end
end
class命令
def调用(参数:,其他:)

如果arg我会在上面再加一个案例什么

间谍给了你一些灵活性。如果需要检查是否调用了该方法,则可以在以下情况下使用模拟:

before do
  expect(foo).to receive(:do_stuff)
end

specify do
  bar.run
end
但是,
之前的
并不是增加期望的最佳场所。你是。您可以这样做:

specify do 
  expect(foo).to receive(:do_stuff)
  bar.run
end
但这样做看起来更好

before do
  bar.run
end

specify do
  expect(foo).to have_received(:do_stuff)
end
当有更多的东西需要检查时,你会有更干净的代码

before { bar.run }

specify do
  expect(foo).to have_received(:do_stuff)
end

context 'with some special conditions' do
  before { set_up_special_conditions }

  specify do
    expect(foo).not_to have_received(:do_stuff)
  end
end
这也许没什么大不了的,你还可以继续生活

specify do
  bar.run
  expect(foo).to have_received(:do_stuff)
end

context 'with some special conditions' do
  before { set_up_special_conditions } # * check the note at the bottom

  specify do
    bar.run
    expect(foo).not_to have_received(:do_stuff)
  end
end
但我认为定义上下文的一个好方法是只明确提到本质上的区别(
set\u-up\u-special\u-conditions
expect(foo)。而不是明确提到接收(:do\u-stuff)
)。任何与“上面”上下文没有区别的内容都不应出现在更具体的“下面”上下文中。它有助于管理更大的规格


*注意:我不确定以这种方式定义的
块之前的
顺序,查看之后,我也不确定该顺序是否得到保证。不能马上检查。但是为了演示的目的,我们可以假设{set\u up\u special\u conditions}之前的
将在{bar.run}
之前运行。但如果不是这样的话——还有其他方法可以确保这一点,但这似乎超出了这个问题的范围。

“您无法设置spy返回的内容”这并不完全正确。您可以使用
allow(spy.to_receive(:msg)
使用任何响应配置机制,在spy上设置响应,就像在double上设置响应一样。文档在感谢中,您是正确的。我的意思是,有了这个接口,你(显然)不能做像这样的事情,
。让_接收(),然后_返回(某物)
,你确实可以,用部分双倍。纯间谍没有原始实现的支持。如果您需要检查原始推进,您必须执行
allow(partial.),以接收(:msg.)和_wrap_original{| m,*args | m.call(*args)。点击{| resp |@resp=resp}
。然后您可以在调用某个对象后对其进行
期望(@resp.)。再说一次,这不是间谍,而是带有包装的局部涂鸦。