Rspec:如何使用expect接收尚不存在的资源?

Joh*_*ith 4 ruby rspec ruby-on-rails

在通过发布请求调用的操作中,我创建资源调用并使用创建的资源作为参数RequestOffer发送电子邮件:ActionMailer

@request_offer = RequestOffer.new(request_offer_params)
if @request_offer.save
    RequestOfferMailer.email_team(@request_offer).deliver_later
end
Run Code Online (Sandbox Code Playgroud)

当我的控制器规范时,我想测试是否使用以资源作为参数的RequestOfferMailer方法调用我的控制器。email_team@request_offer

当我想要 user 时expect(XXX).to receive(YYY).with(ZZZ),我发现的唯一方法是在发出 POST 请求之前声明我的期望。然而,ZZZ是由这个POST请求创建的,所以我之前没有办法设定我的期望。

# Set expectation first
message_delivery = instance_double(ActionMailer::MessageDelivery)

# ZZZ used in .with() does not exist yet, so it won't work
expect(RequestOfferMailer).to receive(:email_team).with(ZZZ).and_return(message_delivery)
expect(message_delivery).to receive(:deliver_later)

# Make POST request that will create ZZZ
post :create, params
Run Code Online (Sandbox Code Playgroud)

知道如何解决这个问题吗?

And*_*dyV 5

如果这是一个功能测试,那么我会将控制器测试与数据库隔离。您可以通过使用instance_doubles和 let 语句来做到这一点。这是一个您可能希望根据您的目的进行扩展的示例

describe '/request_offers [POST]' do
  let(:request_offer) { instance_double(RequestOffer, save: true) }

  before do
    allow(RequestOffer).to receive(:new).
      with(...params...).
      and_return(request_offer)
  end

  it 'should instantiate a RequestOffer with the params' do
    expect(RequestOffer).to receive(:new).
      with(...params...).
      and_return(request_offer)
    post '/request_offers', {...}
  end

  it 'should email the request offer via RequestOfferMailer' do
    mailer = instance_double(ActionMailer::MessageDelivery)

    expect(RequestOfferMailer).to receive(:email_team).
      with(request_offer).and_return(mailer)

    post '/request_offers', {...}
  end
end
Run Code Online (Sandbox Code Playgroud)

关键是使用“let”来声明您想要创建的模型的实例双精度。通过对类设置期望,您可以将实例双重注入测试中并与数据库隔离。请注意,before 块中的“allow”调用是为了服务于对邮件程序对象设置期望的后续规范;第一个测试中的“expect”调用仍然能够对调用做出断言。