Ruby Rspec message expectations for a caught exception - ruby

Is there any way with Rspec to set an expectation for an exception that gets caught? I want to verify that MyException gets raised, but since I am catching the exception Rspec does not appear to know it ever happened.
begin
if success
do good stuff
else
raise MyException.new()
end
rescue MyException => e
clean up
end
I've tried a few things like the following without success. MyException.should_receive(:new) and
Kernel.should_receive(:raise).with(MyException)

You could test the behavior of the rescue block instead of checking for the exception:
class Test
def my_method
if success
# do good stuff
else
raise MyException.new()
end
rescue MyException => e
clean_up
end
end
describe Test do
it "should clean up when unsuccessful" do
subject.stub(:success) { false }
subject.should_receive(:clean_up)
subject.my_method
end
end

I figured out how to do what I needed.
class MyClass
def my_method
begin
if success
do good stuff
else
raise MyException.new
end
rescue MyException => e
# clean up
end
end
end
describe MyClass do
it "Expects caught exception" do
my_instance = MyClass.new()
my_instance.should_receive(:raise).with(any_instance_of(MyException))
my_instance.my_method()
end
end
Thanks for your other suggestions.

I would do as below:
RSpec.describe "matching error message with string" do
it "matches the error message" do
expect { raise StandardError, 'this message exactly'}.
to raise_error('this message exactly')
end
end
copied verbatim from Rspec Documentation

Related

ruby how to stop the execution after rescue

I have a func where when an exception was raised, I am rescuing it.
But the program continues to the next line and calls the next func create_request
But when there is exception, I do not want to continue
def validate_request_code options
if check_everything is good
#code to validate
else
errors << "something is gone bad"
end
[errors.size == 0, errors.size == 0 ? options : raise(ArgumentError, "Error while validating #{errors}")]
end
I am trying to catch/rescue the exception
def validate_request options
begin
validate_request_code options
rescue ArgumentError => e
log :error
rescue Exception => e
log :error
end
sleep 20
if options['action'] == "create"
create_request options
end
end
If by 'not continue' you mean that you want the original error to continue (i.e., you just want to take action on the way by), you can call raise inside the rescue block, which re-raises the original error.
def foo
begin
# stuff
rescue StandardError => e
# handle error
raise
end
end
You can also simply return from within the rescue block as well.
def foo
begin
# stuff
rescue StandardError => e
# handle error
return some_value
end
end
As an aside, generally you want to rescue StandardError rather than Exception. All the things that you can reasonably handle within your application are covered under the StandardError. The things outside that are things like out-of-memory, etc., that are outside your control.

How to use check something before an error is raised

I have the following ruby code
class Gateway
...
def post
begin
...
raise ClientError if state == :open
rescue ClientError => e
Log.add("error")
raise
end
end
end
On RSpec, how can I check that when ClientError is raised Log.add is called?
I have tried different things but I always get the error raised.
Thanks
You can probably do something like this (the initialize step might need to look bit different, depending on how you need to set the state to :open):
describe 'Gateway#post' do
let(:gateway) { Gateway.new(state: :open) }
before { allow(Log).to receive(:add) }
it 'raises an excpetion' do
expect { gateway.post }.to raise_error(ClientError)
expect(Log).to have_received(:add).with('error')
end
end
Something like this should work:
describe '#post' do
context 'with state :open' do
let(:gateway) { Gateway.new(state: :open) }
it 'logs the error' do
expect(Log).to receive(:add).with('error')
gateway.post rescue nil
end
it 're-raises the error' do
expect { gateway.post }.to raise_error(ClientError)
end
end
end
In the first example, rescue nil ensures that your spec is not failing because of the raised error (it silently rescues it). The second example checks that the error is being re-raised.

Abort using custom error instead of SystemExit

I'm working on an application that I had been failing out of using raise, like this:
raise 'Some error happened!'
This caused caused an unsightly stack trace to be displayed, so I adjusted the code to use abort, like this:
abort 'Some error happened!'
Perfect! Now I can exit with a clear message and no stacktrace.
The problem comes in because, in one instance I need to rescue from this situation. I can do something like:
begin
abort 'Some error happened!'
rescue SystemExit
puts 'Rescued'
end
puts 'Moving on...'
# Outputs:
# Some error happened!
# Rescued
# Moving on...
This has the disadvantages of displaying the abort message despite being rescued and rescuing a fairly vague error. What I would really like to do, is something like this:
class MySuperFancyCustomError < StandardError
def initialize
super
abort 'Some error happened!'
end
end
begin
raise MySuperFancyCustomError
rescue MySuperFancyCustomError
puts 'Rescued'
end
puts 'Moving on...'
# Outputs:
# Some error happened!
But I haven't been able to figure out a way to set this up so that I can rescue from it. I just need it to keep running and output:
Rescued
Moving on...
Instead of failing with:
Some error happened!
Does anyone know of an elegant way to make this happen?
I'd strongly advise against using abort for flow control. Instead try this:
class CustomException < StandardError
end
class Example
def explode
raise CustomException, "Michael Bay here!"
end
end
begin
Example.new.explode
rescue CustomException => e
puts "This happened: %s" % e
end
Here you define your own exception type, and then explicitly catch it.
Now you'll get this output, but you have full control over how, if, and when it's displayed:
This happened: Michael Bay here!

Can you rescue from specific errors with messages in ruby?

I'm trying to understand how errors propogate between classes in Ruby. I have this so far:
class User
def charge
puts "charging order soon"
raise RuntimeError.new("This is a runtime error")
rescue ArgumentError
puts "should never gets here"
end
end
class Runner
def run
begin
User.new.charge
rescue RuntimeError => e
puts e.message
end
end
end
Runner.new.run
When I run this, I get this which seems right:
$ ruby errors.rb
charging order soon
This is a runtime error
Inside runner, can I rescue from the RuntimeError with a specific message? If I have multiple RuntimeErrors being raised around my application, is there any way for the Runner's rescue clause to be raised only for RuntimeErrors with specific messages?
See https://stackoverflow.com/a/23771227/2981429
If you call raise inside a rescue block, the last raised exception will be re-raised.
In your exception block, you can check the message and choose to re-raise or not:
begin
User.new.charge
rescue RuntimeError => e
case e.message
when "This is a runtime error"
# put your handler code here
else
raise # re-raise the last exception
end
end
However if it's your goal to solely rescue errors that you yourself raise manually, then it's probably easier to define a custom error class instead:
class MyError < StandardError; end
Then instead of raise RuntimeError.new("message") use raise MyError.new("message"), and rescue it normally:
begin
User.new.charge
rescue MyError => e
# handler
end
This way you don't have to worry about your rescues interfering with the built-in exceptions.

Handle default exception in ruby

How do I implement default behavior for an Exception? begin rescue else doesn't work(which I think should).
And, isn't else meaningless in the scenario? Any code that has to run when no exception is raised does run between the begin-rescue block.
By the way I have the following workaround, but I'm not satisfied with it.
class MyException < Exception
end
class YourException < Exception
end
begin
raise MyException if 2 > 50
raise YourException if 1 < 90
rescue Exception => e
case e.message
when /MyException/
puts "MyException Caught"
else
puts "Default Exception Caught"
end
end
First of all, you really shouldn't subclass Exception. It is the superclass of all Ruby exceptions, including NoMemoryError, SyntaxError, Interrupt, SystemExit; all of which you don't normally need to rescue from. Doing so, whether accidentally or on purpose, is discouraged since it can prevent a program from exiting properly, even if it was interrupted by the user. It can also hide or produce some quite obscure bugs.
What you want to subclass is StandardError, which is the superclass of most Ruby errors we see in day-to-day programming. This class is also the one which will be rescued should you not specify one:
begin
object.do_something!
rescue => error # will rescue StandardError and all subclasses
$stderr.puts error.message
end
I believe this is the "default behavior" you are looking for. You can handle a specific error, then all other errors in general:
class CustomApplicationError < StandardError
end
begin
object.do_something!
rescue CustomApplicationError => error
recover_from error
rescue => error
log.error error.message
raise
end
The else clause is not meaningless in error handling. It will execute the nested code if and only if no exceptions were raised, as opposed to the ensure clause which will execute code regardless. It allows you to handle success cases.
begin
object.do_something!
rescue => error
log.error error.message
else
log.info 'Everything went smoothly'
end
First, I do not understand why you are case-conditioning with the error message. Why not do it with the error itself by their class? Then, it would be like this:
begin
raise MyException if 2 > 50
raise YourException if 1 < 90
rescue Exception => e
case e
when MyException
puts "MyException Caught"
else
puts "Default Exception Caught"
end
end
Second, doing it as above is not the straightforward way. The correct way to do it is:
begin
raise MyException if 2 > 50
raise YourException if 1 < 90
rescue MyException
puts "MyException Caught"
rescue Exception
puts "Default Exception Caught"
end
If YourException were a subclass of StandardError, then it could be captured by rescue without specifying the exception class.

Resources