Rspec 模拟和存根与 expect 混淆
Rspec mocks and stubs confuse with expect
我在 rails 上的 rspec 中使用模拟和存根时感到困惑。我有如下测试
require 'rails_helper'
class Payment
attr_accessor :total_cents
def initialize(payment_gateway, logger)
@payment_gateway = payment_gateway
@logger = logger
end
def save
response = @payment_gateway.charge(total_cents)
@logger.record_payment(response[:payment_id])
end
end
class PaymentGateway
def charge(total_cents)
puts "THIS HITS THE PRODUCTION API AND ALTERS PRODUCTION DATA. THAT'S BAD!"
{ payment_id: rand(1000) }
end
end
class LoggerA
def record_payment(payment_id)
puts "Payment id: #{payment_id}"
end
end
describe Payment do
it 'records the payment' do
payment_gateway = double()
allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234)
logger = double('LoggerA')
expect(logger).to receive(:record_payment).with(1234)
payment = Payment.new(payment_gateway, logger)
payment.total_cents = 1800
payment.save
end
end
好的,当我 运行 rspec 它工作时,没问题,但是当我尝试将 expect
移动到最后一行时,如下所示:
payment = Payment.new(payment_gateway, logger)
payment.total_cents = 1800
payment.save
expect(logger).to receive(:record_payment).with(1234)
我尝试 运行 rpsec,它失败了,我不知道为什么 expect 是最后一行会失败,我认为 expect 总是在我们 运行 得到结果之前放在最后一行去测试。谁能帮我解释一下?
expect(sth).to receive
设置在调用和测试结束之间要满足的消息期望,并在测试完成后验证该期望。当您将 expect
移动到最后一行时,期望值仅在测试结束时设置,并且没有执行任何代码来满足它,因此它失败了。不幸的是,这意味着破坏准备-执行-测试顺序。
这就是为什么你真的应该很少使用 expect.to receive
并将其替换为 allow.to receive
和 expect.to have_received
# prepare
allow(logger).to receive(:record_payment)
# execute
..
# test
expect(logger).to have_received(:record_payment).with(1234)
allow.to receive
设置一个模拟代理,它开始跟踪收到的消息,然后可以由 expect.to have_received
明确验证。一些对象会自动设置它们的模拟代理,例如,对于具有预定义响应的双打,您不需要 allow.to receive
或 spies
。在您的情况下,您可以像这样编写测试:
payment_gateway = double
allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234)
logger = double('LoggerA', record_payment: nil)
payment = Payment.new(payment_gateway, logger)
payment.total_cents = 1800
payment.save
expect(logger).to have_received(:record_payment).with(1234)
其他说明
我强烈建议使用 verifiable_doubles,这将保护您免受误报:
payment_gateway = instance_double(PaymentGateway)
allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234)
如果 PaymentGateway 上没有定义 charge
方法,此测试现在将引发异常 class - 保护您免受测试通过,即使您重命名该方法但忘记重命名测试和实施。
我在 rails 上的 rspec 中使用模拟和存根时感到困惑。我有如下测试
require 'rails_helper'
class Payment
attr_accessor :total_cents
def initialize(payment_gateway, logger)
@payment_gateway = payment_gateway
@logger = logger
end
def save
response = @payment_gateway.charge(total_cents)
@logger.record_payment(response[:payment_id])
end
end
class PaymentGateway
def charge(total_cents)
puts "THIS HITS THE PRODUCTION API AND ALTERS PRODUCTION DATA. THAT'S BAD!"
{ payment_id: rand(1000) }
end
end
class LoggerA
def record_payment(payment_id)
puts "Payment id: #{payment_id}"
end
end
describe Payment do
it 'records the payment' do
payment_gateway = double()
allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234)
logger = double('LoggerA')
expect(logger).to receive(:record_payment).with(1234)
payment = Payment.new(payment_gateway, logger)
payment.total_cents = 1800
payment.save
end
end
好的,当我 运行 rspec 它工作时,没问题,但是当我尝试将 expect
移动到最后一行时,如下所示:
payment = Payment.new(payment_gateway, logger)
payment.total_cents = 1800
payment.save
expect(logger).to receive(:record_payment).with(1234)
我尝试 运行 rpsec,它失败了,我不知道为什么 expect 是最后一行会失败,我认为 expect 总是在我们 运行 得到结果之前放在最后一行去测试。谁能帮我解释一下?
expect(sth).to receive
设置在调用和测试结束之间要满足的消息期望,并在测试完成后验证该期望。当您将 expect
移动到最后一行时,期望值仅在测试结束时设置,并且没有执行任何代码来满足它,因此它失败了。不幸的是,这意味着破坏准备-执行-测试顺序。
这就是为什么你真的应该很少使用 expect.to receive
并将其替换为 allow.to receive
和 expect.to have_received
# prepare
allow(logger).to receive(:record_payment)
# execute
..
# test
expect(logger).to have_received(:record_payment).with(1234)
allow.to receive
设置一个模拟代理,它开始跟踪收到的消息,然后可以由 expect.to have_received
明确验证。一些对象会自动设置它们的模拟代理,例如,对于具有预定义响应的双打,您不需要 allow.to receive
或 spies
。在您的情况下,您可以像这样编写测试:
payment_gateway = double
allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234)
logger = double('LoggerA', record_payment: nil)
payment = Payment.new(payment_gateway, logger)
payment.total_cents = 1800
payment.save
expect(logger).to have_received(:record_payment).with(1234)
其他说明
我强烈建议使用 verifiable_doubles,这将保护您免受误报:
payment_gateway = instance_double(PaymentGateway)
allow(payment_gateway).to receive(:charge).and_return(payment_id: 1234)
如果 PaymentGateway 上没有定义 charge
方法,此测试现在将引发异常 class - 保护您免受测试通过,即使您重命名该方法但忘记重命名测试和实施。