我可以理解Rspec中测试双打的应用。您可能想测试一种方法,该方法具有一个执行昂贵的网络查询的对象。因此,您可以使用填充(测试双精度)从昂贵的操作中返回所需的值:
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
我们需要测试更新方法,但是我们不想测试API查询。因此,我们使用一个测试double并将其存根,以解决需求。
但是后来我遇到了间谍。而且我认为没有用例。这是教程中提供的示例:
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
此特定示例显式调用了三个方法,稍后将检查这三个方法是否被调用。当然,它给他们打电话。它在测试中做对了。在实际情况下,什么时候应该使用间谍?
答案 0 :(得分:1)
间谍会跟踪对其发出的呼叫(特别是发送的消息)。因此,只要需要断言在协作者上进行了特定的呼叫,就可以使用间谍。
典型的用例是根据输入来检查您的实现是否正在使用外部协作者。假设您打算有条件地登录,或者您可能要检查作业是否已包含特定的参数,或者某个邮件程序方法被称为...
间谍是确保对象正确协作的工具。
更新
可以在@meta的答案https://stackoverflow.com/a/52717158/384417中找到示例。
带有代码的一个简单用例是记录器:
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
答案 1 :(得分:0)
间谍为您提供了一定的灵活性。如果您需要检查该方法是否已被调用,则可以在之前的模拟中完成它:
before do
expect(foo).to receive(:do_stuff)
end
specify do
bar.run
end
但是before
并不是增加期望值的最佳场所。您是supposed to separate setup, run and test stages。您可以这样做:
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_up_special_conditions
和expect(foo).not_to have_received(:do_stuff)
)。无论与上下文“上方”没有什么不同,都不应出现在更具体的上下文“下方”中。它有助于管理更大的规格。
*注意:我不确定以这种方式定义的before
块的顺序,在查看rspec docs之后,我不确定该顺序是否得到保证。那一刻无法检查。但是,仅出于演示目的,我们可以假设before { set_up_special_conditions }
将在before { bar.run }
之前运行。但事实并非如此-还有其他方法可以确保这一点,但这似乎超出了此问题的范围。