Here's my begin..rescue..ensure block. I want to write some test cases that after error is raised, the final result {} will be returned.
I am using rspec 3.3.
def external_call
result = ExternalApi.call
rescue => e
# handle the error, and re-raise
Handler.handle(e)
raise
ensure
result.presence || {}
end
I have wrote test case for the rescue part:
context 'when external api raise error' do
it 'handles the error, and re-raise' do
allow(ExternalApi).to receive(:call).and_raise(SomeError)
expect(Handler).to receive(:handle).with(e)
expect { subject.external_call }.to raise_error(SomeError)
end
end
But I am not sure how to test the ensure part after the error is re-raised.
Here's my attempt:
it 'returns {} after error raised' do
allow(ExternalApi).to receive(:call).and_raise(SomeError)
result = subject.external_call
expect(result).to eq({})
end
In this case, the test case will fail in the subject.external_call line, since it will raise error there. I am not sure how to test this cases after the error is re-raised.
When using begin/rescue/ensure block with implicit returns, ruby will return the last method to be run in the rescue block as the return value, not the ensure. If the value from the ensure block needs to be returned, it will either have to be explicitly returned, or not included in an ensure but instead moved outside of the begin/rescue block.
Below is an example which shows the difference.
class TestClass
def self.method1
raise 'an error'
rescue
'rescue block'
ensure
'ensure block'
end
def self.method2
raise 'an error'
rescue
'rescue block'
ensure
return 'ensure block'
end
def self.method3
begin
raise 'an error'
rescue
'rescue block'
end
'ensure equivalent block'
end
end
RSpec.describe TestClass do
it do
# does not work, method1 returns 'rescue block'
expect(TestClass.method1).to eql 'ensure block'
end
it do
# does work, as method2 explicitly returns 'ensure block'
expect(TestClass.method2).to eql 'ensure block'
end
it do
# does work, as method3 uses 'ensure equivalent block' as the inferred return
expect(TestClass.method3).to eql 'ensure equivalent block'
end
end
Related
I am using minitest for tests for my ruby on rails. I have a problem in it, what I was try to do was stubbing a method to return exception. And check if exception raises.
class MyProject
def operations
begin
perform_addtion
rescue StandardError => e
puts e
end
end
def perform_addition
*/ some code */
end
end
I was trying to manually raise exception from perform_addition method like this
def test_my_project
MyProject.any_instance.expects(:perform_addition).raises(StandardError)
assert_raises StandardError do
MyProject.new.operations
end
end
The thing is I can see the stubbed exception is raised and control goes to the rescue block in operations method when I checked with debugger but assert_raises fails and throws StandardError expected but nothing was raised
Can someone tell why its like this. Then how can I check if the rescue is executed
Here's a probably proper solution for this case. I think it's self-explanatory, but feel free to ask questions of any kind
The app:
class MyProject
# you can omit begin, and in methods, you can just write rescue
def operations
perform_addtion
rescue StandardError => e
puts e
# Your rescue "ate" your exception, so you need to raise it again
raise e
end
# add a raise to test
def perform_addition
raise StandardError
end
end
The test:
require 'minitest/autorun'
require 'minitest/mock'
require_relative 'my_project'
class MyProjectTest < Minitest::Test
def test_operations
# create mock for MyProject
mock_my_project = Minitest::Mock.new
# create stub for MyProject
def mock_my_project.operations
raise StandardError
end
# use stub in test
MyProject.new.stub :operations, mock_my_project do
assert_raises StandardError do
MyProject.new.operations
end
end
end
end
I'm able to test that Rollbar.warning has been called, but when it is called in a method that also raises an error, it fails because an error is raised, and it skips Rollbar.warning.
context 'unauthorized' do
before do
allow(Rollbar).to receive(:warning)
end
it 'sends a warning to rollbar' do
subject.send(:log_and_raise_error)
expect(Rollbar).to have_received(:warning)
end
end
Here's the method I'm testing:
def log_and_raise_error
Rollbar.warning(
"Not authorized error accessing resource #{ResourceID}"
)
raise NotAuthorized
end
But when I run specs, it fails with:
1) Authorization unauthorized sends a warning to rollbar
Failure/Error: subject.send(:log_and_raise_error)
NotAuthorized
Any ideas how I can get around that error raising and still test Rollbar?
You can expect the error or rescue it:
expect the error:
it 'sends a warning to rollbar' do
expect { subject.send(:log_and_raise_error) }.to raise_error NotAuthorized
expect(Rollbar).to have_received(:warning)
end
rescue error:
it 'sends a warning to rollbar' do
subject.send(:log_and_raise_error) rescue NotAuthorized
expect(Rollbar).to have_received(:warning)
end
or
it 'sends a warning to rollbar' do
begin
subject.send(:log_and_raise_error)
rescue NotAuthorized
# noop
end
expect(Rollbar).to have_received(:warning)
end
I have the following class, that I am trying to write a spec for:
module IntegrationError
class Error < StandardError; end
class BadRequest < IntegrationError::Error; end
class LogicProblem < IntegrationError::Error; end
def raise_logic_error!(message)
raise IntegrationError::LogicProblem, message
rescue => e
Rails.logger.error e.message
e.backtrace.each do |line|
Rails.logger.error line if line.include?('integrations')
end
end
def raise_bad_request!(message)
raise IntegrationError::BadRequest, message
end
def log_bad_request!(message)
Rails.logger.info message
end
end
with spec
RSpec.describe 'IntegrationError', type: :integration do
let!(:klass) { Class.new { include IntegrationError } }
describe '#log_bad_request!' do
it 'logs it' do
expect(klass.new.log_bad_request!('TESTME')).to be_truthy
end
end
describe '#raise_bad_request!' do
it 'raises it' do
binding.pry
expect(klass.new.raise_bad_request!('TESTME')).to raise_error
end
end
end
the raise_bad_request test returns the error instead of true. Anyone have thoughts on how to write this better to it passes?
I'm using Rails 4 and Rspec 3.4.
If I recall correctly, I believe you need to pass the expectation a block when your raising, like this:
describe '#raise_bad_request!' do
it 'raises it' do
binding.pry
expect{klass.new.raise_bad_request!('TESTME')}.to raise_error
end
end
See docs here
For the raise_error matcher you need to pass a block to expect instead of a value:
expect { klass.raise_bad_request!('TESTME') }.to raise_error
That should do it!
Help me make this test pass:
Here is an example of some rspec code,
class User
attr_accessor :count
def initialize
#count = 0
end
# sometimes raises
def danger
puts "IO can be dangerous..."
rescue IOError => e
#count += 1
end
#always raises
def danger!
raise IOError.new
rescue IOError => e
#count += 1
end
end
describe User do
describe "#danger!" do
it "its rescue block always increases the counter by one" do
allow(subject).to receive(:'danger!')
expect {
subject.danger!
}.to change(subject, :count).by(1)
end
end
describe "#danger" do
context "when it rescues an exception" do
it "should increase the counter" do
allow(subject).to receive(:danger).and_raise(IOError)
expect {
subject.danger
}.to change(subject, :count).by(1)
end
end
end
end
I've also created a fiddle with these tests in it, so you can just make them pass. Please help me test the rescue block of a method!
Background:
My original question went something like this:
I have a method, like the following:
def publish!(resource)
published_resource = resource.publish!(current_project)
resource.update(published: true)
if resource.has_comments?
content = render_to_string partial: "#{ resource.class.name.tableize }/comment", locals: { comment: resource.comment_content_attributes }
resource.publish_comments!(current_project, published_resource.id, content)
end
true
rescue Bcx::ResponseError => e
resource.errors.add(:base, e.errors)
raise e
end
And I want to test that resource.errors.add(:base, e.errors) is, in fact, adding an error to the resource. More generally, I want to test the rescue block in a method.
So I'd like to write code like,
it "collects errors" do
expect{
subject.publish!(training_event.basecamp_calendar_event)
}.to change(training_event.errors.messages, :count).by(1)
end
Of course, this raises an error because I am re-raising in the rescue block.
I've seen a few answers that use the old something.stub(:method_name).and_raise(SomeException), but rspec complains that this syntax is deprecated. I would like to use Rspec Mocks 3.3 and the allow syntax, but I'm having a hard time.
allow(something).to receive(:method_name).and_raise(SomeException)
would be the new allow syntax. Check out the docs for reference.
I was misunderstanding what the allow syntax is actually for. So to make my example specs pass, I needed to do this:
describe "#danger" do
context "when it rescues an exception" do
it "should increase the counter" do
allow($stdout).to receive(:puts).and_raise(IOError) # <----- here
expect {
subject.danger
}.to change(subject, :count).by(1)
end
end
end
This thing that I'm stubing is not the method, or the subject, but the object that might raise. In this case I stub $stdout so that puts will raise.
Here is another fiddle in which the specs are passing.
I have method like this
def className
def method_name
some code
rescue
some code and error message
end
end
So, How to write down the rspec to test rescue block..?
If you want to rescue, it means you expect some code to raise some kind of exception.
You can use RSpec stubs to fake the implementation and force an error. Assuming the execution block contains a method that may raise
def method_name
other_method_that_may_raise
rescue => e
"ERROR: #{e.message}"
end
hook the stub to that method in your specs
it " ... " do
subject.stub(:other_method_that_may_raise) { raise "boom" }
expect { subject.method_name }.to_not raise_error
end
You can also check the rescue handler by testing the result
it " ... " do
subject.stub(:other_method_that_may_raise) { raise "boom" }
expect(subject.method_name).to eq("ERROR: boom")
end
Needless to say, you should raise an error that it's likely to be raised by the real implementation instead of a generic error
{ raise FooError, "boom" }
and rescue only that Error, assuming this is relevant.
As a side note, in Ruby you define a class with:
class ClassName
not
def className
as in your example.
you can stub with return error
for example you have class with method like this :
class Email
def self.send_email
# send email
rescue
'Error sent email'
end
end
so rspec for raising error is
context 'when error occures' do
it 'should return error message' do
allow(Email).to receive(:send_email) { err }
expect(Email.send_email).to eq 'Error sent email brand'
end
end