125

There is the following 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

I want to test it through RSpec. My code is:

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

CarBrand with id equaling 0 doesn't exist, therefore my controller code raises Errors::CarBrandNotFound, but my test code tells me that nothing was raised. How can I fix it? What do I wrong?

Huy Vo
  • 2,418
  • 6
  • 22
  • 43
malcoauri
  • 11,904
  • 28
  • 82
  • 137

4 Answers4

177

Use expect{} instead of expect().

Example:

it do 
  expect { response }.to raise_error(Errors::CarBrandNotFound)
end
Greg
  • 5,862
  • 1
  • 25
  • 52
kaleb4eg
  • 2,255
  • 1
  • 18
  • 13
  • 20
    It's worth pointing out that this solves the issue because the {} syntax creates a block to monitor for the given exception to be raised. MiniTest has similar syntax requirements when checking that a block of code raises an exception. – Argus9 May 15 '17 at 18:34
  • 5
    I can't believe it. 1 hour of trying and it's just turning `( )` into `{ }`! Thank you so much – Simon Franzen Feb 12 '19 at 12:20
  • 1
    I wasn't able to get this method working in Rails 7 – Paul Danelli Jan 30 '22 at 13:00
153

In order to spec error handling, your expectations need to be set on a block; evaluating an object cannot raise an error.

So you want to do something like this:

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

I am a bit surprised that you don't get any exception bubbling up to your spec results, though.

aarona
  • 35,986
  • 41
  • 138
  • 186
Jakob S
  • 19,575
  • 3
  • 40
  • 38
  • 1
    @jakob-s the expect error behaviour that you're using here doesn't work on controller requests. The `get :index, car_brand_id: 0` itself doesn't raise an error. – rikas Mar 11 '15 at 18:00
  • @RicardoOtero Things might have changed, but for what it's worth, it does for me: https://gist.github.com/koppen/0e1d0894a908a3768847. But certainly, something in the stack might be handling the error before it bubbles up to the spec runner. – Jakob S Mar 13 '15 at 09:41
  • This is the right answer for current rspec versions ans should be voted up – Neil Woods Mar 20 '15 at 22:34
  • That is correct. The correct form to to test exceptions is using {} instead of () on except method. – Luiz Henrique Jan 12 '17 at 04:23
18

get :index will never raise an exception - it will rather set response to be an 500 error some way as a real server would do.

Instead try:

it 'raises CarBrandNotFound exception' do
  controller.params[:car_brand_id] = 0
  expect{ controller.car_brand }.to raise_error(Errors::CarBrandNotFound)
end
BroiSatse
  • 44,031
  • 8
  • 61
  • 86
  • 1
    I'm glad that someone mentioned this! :) Jakob tried to educate them that it will never raise an exception in the most upvoted answer but he was faced with some incorrect resistance. – Tarek N. Elsamni Jan 21 '19 at 13:09
4

If you want to test the message that comes along with the exception, you can also do something like this:

it do 
  expect { response }.to raise_error(Errors::CarBrandNotFound, "Ford not found")
end
aarona
  • 35,986
  • 41
  • 138
  • 186