Wie teste ich das Auslösen von Ausnahmen in Rails / RSpec?

84

Es gibt folgenden Code:

def index
    @car_types = car_brand.car_types
end

def car_brand
    CarBrand.find(params[:car_brand_id])
    rescue ActiveRecord::RecordNotFound
        raise Errors::CarBrandNotFound.new 
end

Ich möchte es über RSpec testen. Mein Code lautet:

it 'raises CarBrandNotFound exception' do
    get :index, car_brand_id: 0
    expect(response).to raise_error(Errors::CarBrandNotFound)
end

CarBrand mit einer ID von 0 existiert nicht, daher löst mein Controller-Code Errors :: CarBrandNotFound aus, aber mein Testcode sagt mir, dass nichts ausgelöst wurde. Wie kann ich es reparieren? Was mache ich falsch?

Malcoauri
quelle

Antworten:

115

Um die Fehlerbehandlung zu spezifizieren, müssen Ihre Erwartungen an einen Block gesetzt werden. Das Auswerten eines Objekts kann keinen Fehler auslösen.

Sie möchten also so etwas tun:

expect {
  get :index, car_brand_id: 0
}.to raise_error(Errors::CarBrandNotFound)

Weitere Informationen finden Sie unter Fehler erwarten .

Ich bin ein bisschen überrascht, dass Sie keine Ausnahme bekommen, die zu Ihren Spezifikationsergebnissen sprudelt.

Jakob S.
quelle
1
@ jakob-s Das erwartete Fehlerverhalten, das Sie hier verwenden, funktioniert nicht bei Controller-Anforderungen. Das get :index, car_brand_id: 0selbst löst keinen Fehler aus.
Ricardo Otero
@RicardoOtero Die Dinge mögen sich geändert haben, aber für das, was es wert ist, tut es für mich: gist.github.com/koppen/0e1d0894a908a3768847 . Aber sicherlich könnte etwas im Stapel den Fehler behandeln, bevor er zum Spezifikationsläufer sprudelt.
Jakob S
Dies ist die richtige Antwort für aktuelle rspec-Versionen und sollte abgestimmt werden
Neil Woods
Das ist richtig. Die richtige Form zum Testen von Ausnahmen ist die Verwendung von {} anstelle von () bei der Ausnahme-Methode.
Luiz Henrique
108

Verwenden Sie expect{}anstelle von expect().

kaleb4eg
quelle
6
Es ist erwähnenswert, dass dies das Problem löst, da die {} -Syntax einen Block erstellt, der überwacht, ob die angegebene Ausnahme ausgelöst wird. MiniTest hat ähnliche Syntaxanforderungen, wenn überprüft wird, ob ein Codeblock eine Ausnahme auslöst.
Argus9
1
So eine kleine Veränderung. Ich musste eine Weile meine Räder schleifen. Danke dafür.
Josh
4
Ich kann es nicht glauben. 1 Stunde versuchen und es wird nur ( )in { }! Vielen Dank
Simon Franzen
1
Ja, das gleiche für mich - ich glaube, ich habe mehr ausgegeben :) Aus diesem Grund habe ich hier eine Lösung veröffentlicht
kaleb4eg
2
Ich weiß, dass ich Kommentare verwenden sollte, um zu fragen ... aber dennoch möchte ich Ihnen für die Antwort danken. Nach mehreren Ansätzen fand ich endlich eine Lösung für mein Problem.
Florin Lei
18

get :index wird niemals eine Ausnahme auslösen - es wird eher die Antwort auf einen 500-Fehler setzen, wie es ein echter Server tun würde.

Versuchen Sie stattdessen:

it 'raises CarBrandNotFound exception' do
  controller.params[:car_brand_id] = 0
  expect{ controller.car_brand }.to raise_error(Errors::CarBrandNotFound)
end
BroiSatse
quelle
1
Ich bin froh, dass jemand dies erwähnt hat! :) Jakob versuchte ihnen beizubringen, dass es in der am besten bewerteten Antwort niemals eine Ausnahme geben wird, aber er war mit einem falschen Widerstand konfrontiert. 👍
Tarek N. Elsamni