[英]Rspec mocks and stubs confuse with expect
在軌道上的 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(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
顯式驗證。 一些對象會自動設置它們的模擬代理,例如,對於帶有預定義響應的雙打或spies
,您不需要allow.to receive
。 在您的情況下,您可以編寫如下測試:
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 class 上沒有定義charge
方法,則此測試現在將引發異常 - 即使您重命名該方法但忘記在測試和實現中重命名它,也可以保護您免受測試通過。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.