How to test exception raising in Rails/RSpec? - ruby-on-rails

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?

Use expect{} instead of expect().
Example:
it do
expect { response }.to raise_error(Errors::CarBrandNotFound)
end

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)
See Expect error for details.
I am a bit surprised that you don't get any exception bubbling up to your spec results, though.

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

Related

Rspec for begin ... rescue block in rails

I am writing a test which should verify that when the object creation saves, indeed the exception is caught.
begin
object.create!(item)
rescue => exception
exception.message
end
rspec
expect{ exception.message }.to eq("Validation failed: Item name can't be blank")
output I got:
Failure/Error: expect{ exception.message }.to eq("Validation failed: Item name can't be blank")
You must pass an argument rather than a block to `expect` to use the provided matcher (eq "Validation failed: Item name can't be blank"), or the matcher must implement `supports_block_expectations?`.
Can anyone help me out?
You cannot test your code like that because the exception variable only exists in the rescue block. Instead, you need to call the method you want to test and add an expectation of what it should return.
Your example actually doesn't have such a method that could be called. I imagine that your method actually looks like this:
def do_stuff_with(object, item)
begin
object.create!(item)
rescue => exception
exception.message
end
end
Then you should be able to have an expectation like this:
expect(do_stuff_with(object, item)).to eq(
"Validation failed: Item name can't be blank"
)

RSpec expect something before expect raise_error

Imagine there's a method that rescues and does some logging.
def do_something
# do stuff
some_client.call(var1)
rescue StandardError => e
# log some stuff.
Rails.logger.error("#{self.class} - Var 1 is #{var1}.") if e.is_a?(MyError)
raise
end
Then in the RSpec, I'd like to
assert the error is raised.
it logs the error
before do
allow(Rails.logger).to receive(:error)
allow(some_client).to receive(:call).and_raise(MyError)
end
it "logs the error" do
subject
expect(Rails.logger).to have_received(:error).with(/some message with var1/)
end
it "raises MyError" do
expect { subject }.to raise_error(MyError)
end
expect { subject }.to raise_error(MyError) part is working as expected, but how should I assert the logging? With the example code above, RSpec will report the error on the raised error without asserting the logging.
Just put them both in the same it. Expect that it raises an error and logs it.
it "raises MyError and logs it" do
expect { subject }.to raise_error(MyError)
expect(Rails.logger).to have_received(:error).with(/some message with var1/)
end
Alternatively if you really want to check that it logs the error in a separate it you'll have to rescue the error. Otherwise your spec will fail (unhandled error)
it "logs the error" do
subject
rescue
ensure
expect(Rails.logger).to have_received(:error).with(/some message with var1/)
end

Rspec: Test an exception which is not handled

In my public method #recalculate, calling the private method1. This method throw exception 'StandardError'. I want to test this scenario, however getting an error.
Note: I don't want to handle an exception.
def recalculate
method_1
end
private
def method_1
## do some calculation
raise StandardError.new("Test")
end
Rspec Test case:
it "Test" do
expect { #product.recalculate.recalculate }.to raise_error(StandardError)
#product.recalculate
end
1) Product.Test
Failure/Error: #product.recalculate
StandardError:
Test
(required)>'
Finished in 1.39 seconds
1 example, 1 failure
According to your example, the second line #product.recalculate raises an actual exception hence the error. Assuming recalculate method is defined in #product object, this should be enough to test it.
it "Test" do
expect { #product.recalculate }.to raise_error(StandardError)
end

rails rspec matcher for exception with a stub method

I am having trouble for the matcher to catch the exception. The controller method simply doing a REST call and get the fruit with the id and I want to test when REST give me an error respond which in rails is the JSON::ParserError. I want to test this case, so I stub out the REST call and raise the exception.
I know the fact that the stubbing work since I am getting that exact error. I believe that I just need a matcher to catch the error when calling the get
In Controller
def show
#fruit = FruitsService::Client.get_fruit(params[:id])
end
spec/controller/fruits_controller_spec.rb
describe '#show' do
before do
context 'when a wrong id is given' do
FruitsService::Client.any_instance
.stub(:get_fruit).with('wrong_id')
.and_raise(JSON::ParserError)
end
it 'receives 404 error code' do
get :show, {id: 'wrong_id'} <------ I think I might need a matcher for this ?
expect(FruitsService::Client.get_fruit('wrong_id')).to raise_error(JSON::ParserError)
end
end
This giving this
Failure/Error: get :show, {id: 'wrong_id'}
JSON::ParserError:
JSON::ParserError
When you want to test behavior, such as the raising of errors, you need to pass a block to expect instead of a parameter, as in:
it 'receives 404 error code' do
expect { get :show, {id: 'wrong_id'} }.to raise_error(JSON::ParserError)
end

mocha stubs with exception - rspec test not passing

Hey,
I am trying to use Mocha and Rspec to test a scenario where a method always raises some exception.
Here is the controller code I am trying to test:
def add_parent
begin
parent = Entity.find_by_id(params[:parent_id])
if !parent.nil?
#entity.add_parent!(parent)
flash[:success] = "Entity successfully updated."
else
raise "Parent does not exist."
end
rescue
flash[:error] = "Something bad happened. #{$!}"
end
redirect_to #entity
end
Here is the test code:
it "should flash error if exception is thrown when adding parent" do
Entity.any_instance.stubs(:add_parent!).raises(Exception)
lambda do
post :add_parent, :id => #entity[:id],
:parent_id => #parent_entity[:id]
end.should_not change(#entity.parents, :count)
flash[:error].should =~ /something bad happened/i
end
Here is the method which is being stubbed:
def add_parent!(parent)
Entity.transaction do
lock!('lock in share mode')
self.parents << parent
end
end
I am getting the following rspec error, which is pretty uninformative so I don't know how to resolve it..
Failures:
1) EntitiesController POST 'add_parent' for signed-in users allow access with edit permission should flash error if exception is thrown when adding parent
Failure/Error: post :add_parent, :id => #entity[:id],
Exception
# ./app/controllers/entities_controller.rb:81:in `add_parent'
# ./spec/controllers/entities_controller_spec.rb:1010
Wooah, first of all, it's a very bad habit to rescue from a big chunk of code without give the exception class you are expecting here. rescueing everything is no good. Best remove the whole rescueing, maybe use find over find_by_id so rails can catch that 404 error and you aren't bothered about. On the other hand, is this error supposed to happen a lot? Looks like some db-stuff going on so I wouldn't expect it to fail.
Secondly, I think you must test like the manual says about raise_error
http://relishapp.com/rspec/rspec-expectations/v/2-6/dir/built-in-matchers/raise-error-matcher
rescue with no argument will rescue StandardError and its descendants. If you want to catch ALL exceptions, you should rescue Exception as all exceptions descend from class Exception.
Source: http://www.ruby-forum.com/topic/44495

Resources