Is there a way to rspec test if an error was raised and rescued? If I have a rescue, my rspec test does not see the raised error, only results in the rescue?
module MyApp
def some_method(msg)
raise StandardError.new(msg)
end
def second_method(msg)
begin
count = 0
some_method(msg)
rescue StandardError=> e
puts e
count = 1
end
end
end
RSpec.describe Myapp do
describe "#some_method" do
it "should raise error" do
expect {
some_method("this is an error")
}.to raise_error(StandardError) {|e|
expect(e.message).to eql "this is an error"
}
end
end
# this fails, as the error is not raised
describe "#second_method" do
it should raise error and rescue do
expect {
a = second_method("this is an error and rescue")
}.to raise_error(StandardError) {|e|
expect(e.message).to eql "this is an error and rescue"
expect(a) = 1
}
end
end
end
You generally don't want to raise or rescue StandardError directly because it's pretty uninformative, and won't catch errors outside of the StandardError hierarchy. Instead, you generally want to test that a specific exception was raised, or that a specific error class or error message was raised.
If you know the custom or built-in exception class that you want, or the specific error message, then test for that explicitly. For example:
it 'should raise an ArgumentError exception' do
expect { MyApp.new.foo }.to raise_error(ArgumentError)
end
it 'should raise MyCustomError' do
expect { MyApp.new.foo }.to raise_error(MyCustomError)
end
it 'should raise StandardError with a custom message' do
msg = 'this is a custom error and rescue'
expect { MyApp.new.foo }.to raise_error(msg)
end
If you don't know (or care about) the specific exception or message that should be raised, but you expect some exception to interrupt the execution flow, then you should use a bare raise_error matcher. For example:
it "should raise an exception" do
expect { MyApp.new.foo }.to raise_error
end
Related
I have an rspec where I am testing if an exception thrown from method1 is rescued and raised again in method2 (method2 calls method1). But the RSpec seems to fail. I have debugged through the code and can see the exception being raised.
it 'should check for exceptions raised' do
allow(TestClass).to receive(:method1) do
raise ::Test::CustomException.new(1, 'test.csv.gz', StandardError.new('test exception'))
end
expect(#test_class.method2).to raise_error(Test::CustomException)
end
method2
tester["key_list"].each do |key|
begin
TestClass.method1(key, self.id) do |row|
#do something
end
rescue Test::CustomException => exception
raise ::Test::CustomException.new(self.id, key, exception)
end
end
method1
begin
#do something
rescue => exc
raise ::Test::CustomException.new(id, key, exc)
end
end
CustomException as the name says is a custom defined exception that takes in 3 parameters
Error:
Failures:
should check for exceptions raised
Failure/Error: expect(#test_class.method2).to raise_error
Test::CustomException:
true
test_class.rb:146:in `rescue in block method'
Suppose I have a class with methods like these:
class MyClass
...
def self.some_class_method
my_instance = MyClass.new
self.other_class_method(my_instance)
raise 'ERROR'
end
def self.other_class_method(instance)
...
end
end
And the test for it looks like this:
require 'spec_helper'
describe MyClass do
describe '.some_class_method' do
context 'testing some_class_method' do
it 'calls other_class_method' do
MyClass.should_receive(:other_class_method)
MyClass.some_class_method
end
end
end
end
The test errors out with ERROR, and if I remove the raise 'ERROR' line, the test passes. But here I want to only test whether some_class_method calls other_class_method, regardless of what happens afterwards. I could change it to expect the method to raise an error, but that's not the purpose of this particular test. Is there a better way?
You could rescue the exception in the test.
describe MyClass do
describe '.some_class_method' do
context 'testing some_class_method' do
it 'calls other_class_method' do
MyClass.should_receive(:other_class_method)
begin
MyClass.some_class_method
rescue
end
end
end
end
end
What about adding an expectation that the method is raising an error. That will even enhance your testing:
describe MyClass do
describe '.some_class_method' do
context 'testing some_class_method' do
it 'calls other_class_method' do
expect(MyClass).to receive(:other_class_method)
expect { MyClass.some_class_method }.to raise_error("ERROR")
end
end
end
end
Is there any way to get access to an error message in a rescue block as a string? For example:
def foo
raise RuntimeError, "This is an error"
end
def bar
begin
foo
rescue RuntimeError
puts "Rescued"
end
end
bar
Is there any way to gain access to "This is an error" from with-in the rescue block? Something like this:
...
rescue RuntimeError
puts <error-message>
end
...
You need to specify a variable to store the error in
def foo
raise RuntimeError, "This is an error"
end
def bar
begin
foo
rescue RuntimeError => ex
puts "Rescued #{ex.message}"
end
end
How can I test with RSpec some code with #exit!?
def method
...
rescue MyError => e
logger.error "FATAL ERROR"
exit! 1
end
I can test this code with #exit method because raise the SystemExit exception.
it "logs a fatal error" do
lambda do
object.method
expect(logger).to have_received(:error).with("FATAL ERROR")
end
end
it "exits" do
expect { object.method }.to raise_error(SystemExit)
end
I'm not sure if I can achieve something similar. I'm thinking to reimplement the exit! method in Kernel module, just only for the specs. Any ideas?
You can stub the exit! method for the object:
it "logs a fatal error" do
lambda do
allow(object).to receive(:exit!)
object.method
expect(logger).to have_received(:error).with("FATAL ERROR")
end
end
it "exits" do
expect(object).to receive(:exit!)
object.method
end
I'm looking for something like this:
raise Exception rescue nil
But the shortest way I've found is this:
begin
raise Exception
rescue Exception
end
This is provided by ActiveSupport:
suppress(Exception) do
# dangerous code here
end
http://api.rubyonrails.org/classes/Kernel.html#method-i-suppress
def ignore_exception
begin
yield
rescue Exception
end
end
Now write you code as
ignore_exception { puts "Ignoring Exception"; raise Exception; puts "This is Ignored" }
Just wrap the left-hand side in parenthesis:
(raise RuntimeError, "foo") rescue 'yahoo'
Note that the rescue will only happen if the exception is a StandardError or a subclass thereof. See http://ruby.runpaint.org/exceptions for more info.