簡體   English   中英

Rspec 模擬和存根與期望混淆

[英]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 receiveexpect.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM