How to handle exception in if condition using watir - ruby

I want to check whether span with id = "Error_Title" exists or not. I am getting time out exception due to this condition. I do not know where the exception should be handled in this type of condition.
if(ie.span(:id,"Error_Title").exists?)
puts "yes it is there"
end

According to How can I avoid program termination caused by watir-webdriver timeout error while loading a webpage? , this should work(untested):
begin
if(ie.span(:id,"Error_Title").exists?)
puts "yes it is there"
end
rescue Timeout::Error
puts "Gotcha!"
end

Related

Is there a way to abort a test cleanly?

Using Selenium, Ruby
I'm trying to learn the correct way of closing out a test should an object not exist. For example I have a test that calls a function "Click_Login" which in turn goes to the function and returns the object reference or the text "Stop Test" if it does not exist. That part is working correctly.
However after the browser is closed, the test continues on and tries to varLoginBtn.click and fails because Stop Test.click does not exist. I thought the test would have stopped after the driver.quit and not continue on to varLoginBtn.click.
My goal is to have the test stop cleanly if an object does not exist. I maybe doing this incorrectly.
#The test.rb
varLoginBtn = toNavigate.Click_LogIn(driver) #calls function down below.
if varLoginBtn == "Stop Test"
driver.quit
end
varLoginBtn.click #click on the button
#functions.rb
#in the Class toNavigate
#Login Button
def Click_LogIn(driver)
puts "In the login"
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
begin
element= wait.until{driver.find_element(:xpath, "//*[#xas-string='SDE_LOG_INN']")} #Log_INN is intentional I want it to fail.
return element
rescue
puts "The Login button did not exist"
return "Stop Test"
end
end
Thanks for your help.
You don't need to rescue, you have a condition if nil and you can use abort to exit script with a message
But also use snake_case for def ruby methods
def click_login(driver)
puts "In the login"
wait = Selenium::WebDriver::Wait.new(:timeout => 15)
if element = wait.until{driver.find_element(:xpath, "//*[#xas-string='SDE_LOG_INN']")} #Log_INN is intentional I want it to fail.
return element
else
puts 'The Login button did not exist'
abort 'Stop Test'
end
end
This is an overengineered way to do this... why not just throw an exception when this occurs and let the test die with a good message? Something like
...
rescue
raise "The Login button did not exist"
end
Your test library should be able to handle this and print a nice message that you can use to investigate, etc.
Read more about exceptions in ruby here.

Reraise (same exception) after catching an exception in Ruby

I am trying to improve my Ruby skills by catching exceptions. I want to know if it is common to reraise the same kind of exception when you have several method calls. So, would the following code make sense? Is it ok to reraise the same kind of exception, or should I not catch it on the process method?
class Logo
def process
begin
#processed_logo = LogoProcessor::create_image(self.src)
rescue CustomException
raise CustomException
end
end
end
module LogoProcessor
def self.create_image
raise CustomException if some_condition
end
end
Sometimes we just want to know an error happened, without having to actually handle the error.
It is often the case that the one responsible for handling errors is user of the object: the caller. What if we are interested in the error, but don't want to assume that responsibility? We rescue the error, do whatever we need to do and then propagate the signal up the stack as if nothing had happened.
For example, what if we wanted to log the error message and then let the caller deal with it?
begin
this_will_fail!
rescue Failure => error
log.error error.message
raise
end
Calling raise without any arguments will raise the last error. In our case, we are re-raising error.
In the example you presented in your question, re-raising the error is simply not necessary. You could simply let it propagate up the stack naturally. The only difference in your example is you're creating a new error object and raising it instead of re-raising the last one.
This will raise the same type of error as the original, but you can customize the message.
rescue StandardError => e
raise e.class, "Message: #{e.message}"
I had the same question as in the comment thread here, i.e. What if the line before (re)raise fails?
My understanding was limited by the missing knowledge that the global variable of $! is "kinda garbage collected" // "scoped to its functional context", which the below example demonstrates:
def func
begin
raise StandardError, 'func!'
rescue StandardError => err
puts "$! = #{$!.inspect}"
end
end
begin
raise StandardError, 'oh no!'
rescue StandardError => err
func
puts "$! = #{$!.inspect}"
raise
end
The output of the above is:
$! = #<StandardError: func!>
$! = #<StandardError: oh no!>
StandardError: oh no!
from (pry):47:in `__pry__'
This behavior is different than how Python's (re)raise works.
The documentation for Exception states:
When an exception has been raised but not yet handled (in rescue,
ensure, at_exit and END blocks), two global variables are set:
$! contains the current exception.
$# contains its backtrace.
So these variables aren't true global variables, they are only defined inside the block that's handling the error.
begin
raise
rescue
p $! # StandardError
end
p $! # nil
A slightly better way to do the same thing as FreePender is to use the exception method from the Exception class, which is the ancestor class to any error classes, like StandardError, so that the method is available to any error classes.
Here the method's documentation that you can find on ApiDock:
With no argument, or if the argument is the same as the receiver, return the receiver. Otherwise, create a new exception object of the same class as the receiver, but with a message equal to string.to_str.
Now let's see how it works:
begin
this_will_fail!
rescue Failure => error
raise error.exception("Message: #{error.message}")
end
Adding to above answers here:
In some applications you may need to log the error twice.
For example exception need to be notified to monitoring tools like
Nagios/Newrelic/Cloudwatch.
At the same time you may have your own kibana backed summary logging
tool, internally for your reference.
In those cases you might want to log & handle the errors multiple times.
Example:
begin
begin
nil.to_sym
rescue => e
puts "inner block error message: #{e.message}"
puts "inner block backtrace: #{e.backtrace.join("\n")}"
raise e
end
rescue => e
puts "outer block error message: #{e.message}"
puts "outer block backtrace: #{e.backtrace.join("\n")}"
end
I am using puts here, for your the ease of verifying this code in
rails console, in actual production you may need to use rails logger

Which system signal is sent to a ruby program when an exception is raised and the program stops execution?

Any time my program stops execution (either when shut down by cmd-c or when it encounters an exception), I want to take a few actions to shut down properly.
When I do cmd-c, I receive the signal TERM. What signal is sent when the program encounters an exception that is raised? How do I trap this with Signal.trap(...)?
You could wrap your code in a begin-ensure-end block. It would catch exceptions and CTRL-C. (You could add a rescue clause before the ensure).
begin
sleep 10 #try CTRL-C here
raise "kaboom" #RuntimeError
ensure
puts "This must be printed no matter what."
end
An exception is not a signal. The Ruby interpreter handles exceptions all in user code; there's nothing to trap.
If you want to handle exceptions, you need to do so in a rescue block.
You can't catch the exception as a signal, but you can do something when it's raised using the 'EXIT' signal:
Signal.trap('EXIT') do
puts "Terminating..."
shutdown()
end
However, I just stated that you can do this; you really should use begin and rescue.
The point wit exceptions is not trapping the signal via Signal.trap but rather wrapping the code that may raise an exception in a begin-rescue-end block. You have more Options though:
begin
# here goes the code that may raise an exception
rescue ThisError
# this code is executed when 'ThisError' was raised
rescue ThatError, AnotherError
# this code is executed when 'ThatError' or 'AnotherError' was raised
rescue
# this code is executed when any other StandardError was raised
else
# this code is executed when NO exception was raised
ensure
# this code is always executed
end
Here are some bit more practical examples of how to use this:
def compute_something(x,y)
raise ArgumentError, 'x must not be lower than 0' if x < 0
x/y + y
end
begin
compute_something(-10,5)
rescue ArgumentError
puts "some argument is erroneous!"
end
puts "---"
x=100
y=0
begin
compute_something(x,y)
rescue ZeroDivisionError
puts "division by zero! trying to fix that..."
y=1
retry
else
puts "everything fine!"
end
puts "---"
begin
compute_something(1)
rescue => e
puts "the following error occured:"
puts e
end
puts "---"
begin
exit
ensure
puts "i am always called!"
end
this outputs:
some argument is erroneous!
---
division by zero! trying to fix that...
everything fine!
---
the following error occured:
wrong number of arguments (1 for 2)
---
i am always called!
As an alternative to the above solutions, you could look into the at_exit method.

Ruby Exceptions -- Why "else"?

I'm trying to understand exceptions in Ruby but I'm a little confused. The tutorial I'm using says that if an exception occurs that does not match any of the exceptions identified by the rescue statements, you can use an "else" to catch it:
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
However, I also saw later in the tutorial "rescue" being used without an exception specified:
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
If you can do this, then do I ever need to use else? Or can I just use a generic rescue at the end like this?
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
rescue
# Other exceptions
ensure
# Always will be executed
end
The else is for when the block completes without an exception thrown. The ensure is run whether the block completes successfully or not. Example:
begin
puts "Hello, world!"
rescue
puts "rescue"
else
puts "else"
ensure
puts "ensure"
end
This will print Hello, world!, then else, then ensure.
Here's a concrete use-case for else in a begin expression. Suppose you're writing automated tests, and you want to write a method that returns the error raised by a block. But you also want the test to fail if the block doesn't raise an error. You can do this:
def get_error_from(&block)
begin
block.call
rescue => err
err # we want to return this
else
raise "No error was raised"
end
end
Note that you can't move the raise inside the begin block, because it'll get rescued. Of course, there are other ways without using else, like checking whether err is nil after the end, but that's not as succinct.
Personally, I rarely use else in this way because I think it's rarely needed, but it does come in handy in those rare cases.
EDIT
Another use case occurred to me. Here's a typical begin/rescue:
begin
do_something_that_may_raise_argument_error
do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
handle_the_error
end
Why is this less than ideal? Because the intent is to rescue when do_something_that_may_raise_argument_error raises ArgumentError, not when do_something_else_when_the_previous_line_doesnt_raise raises.
It's usually better to use begin/rescue to wrap the minimum code you want to protect from a raise, because otherwise:
you may mask bugs in the code that wasn't supposed to raise
the intention of rescue is harder to decipher. Someone (including your future self) may read the code and wonder "Which expression did I want to protect? It looks like expression ABC... but maybe expression DEF too???? What was the author intending?!" Refactoring becomes much more difficult.
You avoid those problems with this simple change:
begin
do_something_that_may_raise_argument_error
rescue ArgumentError => e
handle_the_error
else
do_something_else_when_the_previous_line_doesnt_raise
end
The else block in a begin rescue end block is used when you are perhaps expecting an exception of some sort to occur. If you run through all of your expected exceptions but still have nothing raised, then in your else block you can do whatever's needed now that you know that your original code ran error free.
The only reason I can see for the else block is if you want to execute something before the ensure block when the code in the begin block didn't raise any errors.
begin
puts "Hello"
rescue
puts "Error"
else
puts "Success"
ensure
puts "my old friend"
puts "I've come to talk with you again."
end
Thanks to else you sometimes can merge two nested begin end blocks.
So (simplified example from my current code) instead of:
begin
html = begin
NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
end
redo unless html["market"]
end
you write:
begin
html = NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
else
redo unless html["market"]
end

Capturing Ctrl-c in ruby

I was passed a long running legacy ruby program, which has numerous occurrences of
begin
#dosomething
rescue Exception => e
#halt the exception's progress
end
throughout it.
Without tracking down every single possible exception these each could be handling (at least not immediately), I'd still like to be able to shut it down at times with CtrlC.
And I'd like to do so in a way which only adds to the code (so I don't affect the existing behavior, or miss an otherwise caught exception in the middle of a run.)
[CtrlC is SIGINT, or SystemExit, which appears to be equivalent to SignalException.new("INT") in Ruby's exception handling system. class SignalException < Exception, which is why this problem comes up.]
The code I would like to have written would be:
begin
#dosomething
rescue SignalException => e
raise e
rescue Exception => e
#halt the exception's progress
end
EDIT: This code works, as long as you get the class of the exception you want to trap correct. That's either SystemExit, Interrupt, or IRB::Abort as below.
The problem is that when a Ruby program ends, it does so by raising SystemExit. When a control-C comes in, it raises Interrupt. Since both SystemExit and Interrupt derive from Exception, your exception handling is stopping the exit or interrupt in its tracks. Here's the fix:
Wherever you can, change
rescue Exception => e
# ...
end
to
rescue StandardError => e
# ...
end
for those you can't change to StandardError, re-raise the exception:
rescue Exception => e
# ...
raise
end
or, at the very least, re-raise SystemExit and Interrupt
rescue SystemExit, Interrupt
raise
rescue Exception => e
#...
end
Any custom exceptions you have made should derive from StandardError, not Exception.
If you can wrap your whole program you can do something like the following:
trap("SIGINT") { throw :ctrl_c }
catch :ctrl_c do
begin
sleep(10)
rescue Exception
puts "Not printed"
end
end
This basically has CtrlC use catch/throw instead of exception handling, so unless the existing code already has a catch :ctrl_c in it, it should be fine.
Alternatively you can do a trap("SIGINT") { exit! }. exit! exits immediately, it does not raise an exception so the code can't accidentally catch it.
If you can't wrap your whole application in a begin ... rescue block (e.g., Thor) you can just trap SIGINT:
trap "SIGINT" do
puts "Exiting"
exit 130
end
130 is a standard exit code.
I am using ensure to great effect! This is for things you want to have happen when your stuff ends no matter why it ends.
Handling Ctrl-C cleanly in Ruby the ZeroMQ way:
#!/usr/bin/env ruby
# Shows how to handle Ctrl-C
require 'ffi-rzmq'
context = ZMQ::Context.new(1)
socket = context.socket(ZMQ::REP)
socket.bind("tcp://*:5558")
trap("INT") { puts "Shutting down."; socket.close; context.terminate; exit}
puts "Starting up"
while true do
message = socket.recv_string
puts "Message: #{message.inspect}"
socket.send_string("Message received")
end
Source
Perhaps the most simple solution?
Signal.trap('INT') { exit }
This is what I use, it works. Put it somewhere before a possible user interaction.
Here, a more verbose solution, to print something to STDERR and exit:
Signal.trap('INT') { abort 'Interrupted by user' }
See here for difference between exit and abort.

Resources