test coverage for rescue blocks in rspec - ruby-on-rails

I am trying to get coverage on the following sections of code (from begin to end)in my attached spec where
def process_request(data)
agents = agent_emails(data)
begin
ej = EmailerJob.new(agents: agents, data: data)
CommonLogger.input_log(log_obj,"Sending Lead email to #{agents}")
ej.deliver_emails
rescue Timeout::Error => error
CommonLogger.input_log(log_obj,"#{error}","error")
raise error
rescue StandardError => error
Rails.logger.error "StandardError Handling for #{agents}"
CommonLogger.input_log(log_obj,"#{error}","error")
rescue Exception => error
Rails.logger.error "Exception Handling for #{agents}"
CommonLogger.input_log(log_obj,"#{error}","error")
raise error
end
end
coverage report:
Can somebody please help me how can i cover all rescue sections?
Rspec: 3.12.0

Related

How to write RSpec test cases for TimeOutError Exception

I have written retry logic for sending mails and trying to cover test cases for it.
def send_mail(mail,log_obj)
begin
attempts ||= 1
mail.deliver_now
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Net::OpenTimeout, Net::ReadTimeout => e
CommonLogger.input_log(log_obj,"Timeout: #{e} (attempt ##{ attempts })")
if (attempts += 1) <= 4
sleep(1)
Rails.logger.info ("Retrying...")
retry
else
Rails.logger.error("Retry attempts exceeded.")
raise e
end
end
end
You can use and_raise to raise errors
https://relishapp.com/rspec/rspec-mocks/docs/configuring-responses/raising-an-error
allow(mail).to receive(:deliver_now).and_raise(Net::OpenTimeout)
allow_any_instance_of(MailClass).to receive(:deliver_now).and_raise(Net::OpenTimeout)

Is it possible for begin .. rescue to NOT catch an exception?

I have a process that is within a begin rescue loop, that looks like this:
begin
# do some stuff
rescue Exception => e
Rails.logger.info "#{e.response.message}"
end
Is it possible for this to NOT catch an exception? For some reason my process is running, not throwing errors, but randomly not working.
Just use :
# do some stuff
without any begin/rescue block, and see which Error comes out. Let's say it is NoMethodError. Maybe you have a typo in some of your code, like "abc".spilt.
Correct it. Try again, maybe you get Errno::ECONNRESET.
Try :
begin
# do some stuff
rescue Errno::ECONNRESET => e
Rails.logger.info "#{e.message}"
end
Rinse and repeat, but start from scratch. rescue Exception is just too much.
Maybe you can temporary comment rescue block:
#begin
...
#rescue Exception => e
#Rails.logger.info "#{e.response.message}"
or you can raise raise this exception in rescue:
begin
do some stuff
rescue Exception => e
Rails.logger.info "#{e.response.message}"
raise e
end

How to rescue Socket Error in Ruby with RestClient?

I am using RestClient to make a network call in the ruby class. I am getting a SocketError whenever I am not connected to the internet. I have added a rescue block to catch the exception still I am not able to do so.
the error message is:
SocketError (Failed to open TCP connection to api.something.com:443 (getaddrinfo: Name or service not known))
module MyProject
class Client
def get_object(url, params={})
response = RestClient.get(url, {params: params})
rescue SocketError => e
puts "In Socket errror"
rescue => e
puts (e.class.inspect)
end
end
end
The broad rescue gets called and print SocketError, but why the previous rescue SocketError is not triggered!
Do you see something that I am missing?
There are a couple of exceptions you'll need to rescue in case you want to fail gracefully.
require 'rest-client'
def get_object(url, params={})
response = RestClient.get(url, {params: params})
rescue RestClient::ResourceNotFound => e
p e.class
rescue SocketError => e
p e.class
rescue Errno::ECONNREFUSED => e
p e.class
end
get_object('https://www.google.com/missing')
get_object('https://dasdkajsdlaadsasd.com')
get_object('dasdkajsdlaadsasd.com')
get_object('invalid')
get_object('127.0.0.1')
Because you can have problems regarding the uri being a 404 destination, or be a domain that doesn't existing, an IP, and so on.
As you can see above, you can have different scenarios that you may encounter when dealing with connecting to a remote uri. The code above rescue from the most common scenarios.
The first one the URL is missing, which is handled by RestClient itself.
The the following three below are invalid domains, which will fail with SocketError (basically a DNS error in this case).
Finally in the last call we try to connect to an IP that has no server running on it - therefore it throws an ERRNO::ECONNREFUSED

RSpec: force Net::SFTP::StatusException and validate rescue

I'm trying to force an Net::SFTP::StatusException error in my spec and then validate that my code traps it.
Code:
def process_SFTP(username, password, csvfile)
begin
mySftpWrapper.new
mySftpWrapper.process_CSV_file(csvfile)
rescue RuntimeError => other_problem
logger.info(other_problem.message)
rescue Net::SFTP::StatusException => sftp_problem
logger.info(sftp_problem.message)
end
end
RSpec:
let(:wrapper) { mySftpWrapper.new }
before(:all) do
#wrapper_mock = double('mySftpWrapper')
mySftpWrapper.stub(:new).and_return(#wrapper_mock)
#logger_mock = double('ActiveSupport::BufferedLogger')
end
it "should trap a Net::SFTP::StatusException" do
sftp_response = Object.new
def sftp_response.code; code = 2 end
def sftp_response.message; message = "no such file" end
# Force exception here
#wrapper_mock.stub(:process_SFTP).with("username", "password", "nonexistentfile").and_raise(Net::SFTP::StatusException.new(sftp_response))
# Verify that logger.info received the forced error
#logger_mock.stub(:info).should_receive(/no such file/)
wrapper.process_SFTP("date", "username", "password", "nonexistentfile")
end
However, the statement I use to generate the Net::SFTP::StatusException doesn't throw the correct exception ("Net::SFTP::StatusException"). Instead it throws #<Net::SFTP::StatusException: Net::SFTP::StatusException>
How do I force the correct StatusException?
Any ideas are much appreciated!
As it turns out, the spec example is throwing exactly the correct Exception. The problem is that the Net::SFTP::StatusException is subclassed from RuntimeError:
net-sftp-2.0.5/lib/net/sftp/errors.rb:
module Net; module SFTP
# The base exception class for the SFTP system.
class Exception < RuntimeError; end
# A exception class for reporting a non-success result of an operation.
class StatusException < Net::SFTP::Exception
and the Net::SFTP::StatusException was getting thrown, but it was getting trapped by the RuntimeError clause before reaching the StatusException clause. Exactly what a spec is for!

How to check for specific rescue clause for error handling in Rails 3.x?

I have the following code:
begin
site = RedirectFollower.new(url).resolve
rescue => e
puts e.to_s
return false
end
Which throws errors like:
the scheme http does not accept registry part: www.officedepot.com;
the scheme http does not accept registry part: ww2.google.com/something;
Operation timed out - connect(2)
How can I add in another rescue for all errors that are like the scheme http does not accept registry part?
Since I want to do something other than just printing the error and returning false in that case.
That depends.
I see the three exception descriptions are different. Are the Exception types different as well?
If So you could write your code like this:
begin
site = RedirectFollower.new(url).resolve
rescue ExceptionType1 => e
#do something with exception that throws 'scheme http does not...'
else
#do something with other exceptions
end
If the exception types are the same then you'll still have a single rescue block but will decide what to do based on a regular expression. Perhaps something like:
begin
site = RedirectFollower.new(url).resolve
rescue Exception => e
if e.message =~ /the scheme http does not accept registry part/
#do something with it
end
end
Does this help?
Check what is exception class in case of 'the scheme http does not accept registry part' ( you can do this by puts e.class). I assume that this will be other than 'Operation timed out - connect(2)'
then:
begin
.
rescue YourExceptionClass => e
.
rescue => e
.
end
Important Note: Rescue with wildcard, defaults to StandardError. It will not rescue every error.
For example, SignalException: SIGTERMwill not be rescued with rescue => error. You will have to specifically use rescue SignalException => e.

Resources