2016-04-14 6 views
0

In einer Aktion über eine Post-Anforderung rief ich eine Ressource nennt RequestOffer bin zu schaffen und eine E-Mail mit Action mit der erstellte Ressource als Parameter senden:Rspec: Wie verwendet man erwarten, mit einer Ressource zu erhalten, die noch nicht existiert?

@request_offer = RequestOffer.new(request_offer_params) 
if @request_offer.save 
    RequestOfferMailer.email_team(@request_offer).deliver_later 
end 

Wenn meine Controller-Spezifikation, ich testen will, dass RequestOfferMailer wird mit der Methode email_team mit der Ressource @request_offer als Parameter aufgerufen.

Wenn ich Benutzer erwarten (XXX). Zu empfangen (YYY) .with (ZZZ), die einzige Möglichkeit, die ich gefunden habe, war meine Erwartung zu erklären, bevor Sie die POST-Anfrage. ZZZ wird jedoch durch diese POST-Anfrage erstellt, sodass ich meine Erwartungen vorher nicht festlegen kann.

# 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 

Irgendeine Idee, wie man dieses Problem löst?

Antwort

1

Wenn dies ein Funktionstest ist dann würde ich den Controller-Test von der DB isolieren. Sie können dies tun, indem Sie instance_doubles und let-Anweisungen verwenden. Hier ist ein Beispiel, das Sie mögen können für Ihre Zwecke

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 

Der Schlüssel dazu verlängern wird mit 'lass uns eine Instanz Doppel des Modells zu erklären, die Sie erstellen möchten. Indem Sie Erwartungen an die Klasse stellen, können Sie Ihre Instanz doppelt in den Test injizieren und aus der DB isolieren. Beachten Sie, dass der Aufruf "allow" im before-Block den späteren Spezifikationen dient, die Erwartungen an das Mailer-Objekt setzen. Der "Expect" -Aufruf im ersten Test kann weiterhin Aussagen über den Anruf machen.

1

Wäre es ausreichend sicherzustellen, dass das Argument eine Instanz von RequestOffer ist? Dann könnten Sie den instance_of Matcher verwenden. Zum Beispiel:

expect(RequestOfferMailer).to receive(:email_team).with(instance_of(RequestOffer)).and_return(message_delivery) 

fand ich diese Option in dem Rspec 3.0 docs: https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/setting-constraints/matching-arguments

+0

Ich würde idealerweise mehr als das testen. Allerdings bietet Ihr Link etwas, das eine Lösung sein könnte: https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/setting-constraints/matching-arguments#using-a- custom-matcher Ich könnte eine Überprüfung definieren, dass es sich um eine Instanz eines RequestOffer handelt, indem auch die wichtigen Attribute wie phone_number und email in meinem Fall, die in der Vorlage der E-Mail benötigt werden, gleich sind. Mal sehen ob jemand eine einfachere Lösung hat, aber danke schon für den Hinweis! –

Verwandte Themen