how to force a cucumber scenario to fail? - ruby

Is there a way to force a cucumber scenario to fail?
I need to check for a few failing scenarios at the end of each of my tests. So I thought I could do the check for an 'error' dialog and then fail the test if it occurred.
This is possible with the code below however there is a problem. Once I raise the exception in the fail! function, then cucumber stops running the rest of the After hook, so the logout function doesnt get called.
Was:
After() do |scenario|
#Checking for Error popups
if page.has_selector?(:dialog_message, 1, :text => 'Error')
fail!(raise(ArgumentError.new('Unexpected Error dialog!')))
end
logout
end
Now:
After() do |scenario|
#Checking for Error popups
if page.has_selector?(:dialog_message, 1, :text => 'Error')
scenario.fail!(logout)
end
end
Is there a better way to fail a cucumber test without raising an exception?

You can get the after hook to fail using your normal assertions. Have not done much with Capybara/rspec exceptions, but I think you can do:
page.should have_selector?(:dialog_message, 1, :text => 'Error')
However, if you do this or do the scenario.fail!(), you will still not logout. You need to wrap it in a begin-ensure block.
Try this:
After do |scenario|
begin
page.should have_selector?(:dialog_message, 1, :text => 'Error')
ensure
logout
end
end
Update
If you do not want to call the standard assertions and directly fail the scenario, you can do the following - you need to use fail instead of fail!:
After() do |scenario|
begin
#Checking for Error popups
if page.has_selector?(:dialog_message, 1, :text => 'Error')
fail(ArgumentError.new('Unexpected Error dialog!'))
#Or you can just do fail('Unexpected Error dialog') if you do not care about the type.
end
ensure
logout
end
end

Just a preliminary answer as I haven't been able to check it out yet, but I'd assume that calling Raise is always guaranteed to halt execution (unless it's done inside a proc or lambda so it's evaluation is deferred).
Try simply
if page.has_selector?(:dialog_message, 1, :text => 'Error')
scenario.fail!(StandardError.new('Unexpected Error Dialog!'))
end
logout

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.

Checking if test failed and giving console output accordingly

I am new to ruby and would like to add a step in my ruby/capybara test where
"if Test A failed,
log this message in the console: "Microservice A is currently down."
Should this be done in the after hook or inside the test? Also, what would the commands be?
describe 'Test Description' do
before (:each) do
login end
after (:each) do
logout
if test fail do
console.log ("Error: Microservice A currently is down")
end
end
it 'Check Page X Loads', :retry => 3, :retry_wait => 3 do
page.should have_content 'Frisbee'
navigate_to_menu 'Toys'
page.has_content?("Frisbee")
expect(page).to have_content('Buy Frisbee') end
end
Thank you
The after hook receives the test that was run as a parameter so you can do
after do |example|
if example.exception
puts "Error: Microservice A currently is down"
end
end

.visible? doesn't give false when element not visible

There are test cases where I want to check:
Load more button visible
Load more not visible
I wrote this method:
def loadmore_button_visible?
wait_until(20) do
#browser.refresh
link_element(:title => 'load_more').visible?
end
end
and used it as
expect(on(ProductViewPage).loadmore_button_visible?).to be_true for "test1"
and expect(on(ProductViewPage).loadmore_button_visible?).to be_false for "test2"
It works test1 but for test2 it gives Time Out Error. I think I have asked similar question here wait_until block is giving time out error
but this time I think its not about wait_until block as wait_until works for "test1".
Your wait_until block is expecting to resolve to true. If it does not, it will raise a TimeoutError. The first test passes because link_element(:title => 'load_more').visible? is true and that satisfies the wait_until method. If you want the method to return true and false, you need to catch the error and explicitly return false. Something like:
def loadmore_button_visible?
begin
wait_until(20) do
#browser.refresh
link_element(:title => 'load_more').visible?
end
rescue TimeoutError
false
end
end

How to display payment gateway response messages in view - Rails 4, Active Merchant

I'm using Active Merchant with Stripe as the payment gateway. Everything works fine except that i don't know how to go about getting the gateway response error messages from Stripe (when a card is declined, invalid etc) to display on the checkout page to the user. I can get a StandardError to be raised that redirects to an error page with the response message but that's it.
ORDER MODEL
class Order < ActiveRecord::Base
has_many :order_products
has_many :products, through: :order_products
attr_accessor :card_number, :security_code, :card_expires_on
validate :validate_card, :on => :create
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors[:base] << message
end
end
end
def purchase(basket)
response = GATEWAY.purchase(Product.total_basket_price(basket)*100, credit_card, purchase_options)
unless response.success?
raise StandardError, response.message
end
end
def credit_card
#credit_card ||= ActiveMerchant::Billing::CreditCard.new(
:number => card_number,
:first_name => first_name,
:last_name => last_name,
:verification_value => security_code,
:month => card_expires_on.month,
:year => card_expires_on.year
)
end
def purchase_options
{
:billing_address => {
:address1 => address_1,
:address2 => address_2,
:city => city,
:country => country_code,
:zip => postal_code
}
}
end
end
ORDERS CONTROLLER
class OrdersController < ApplicationController
def create
#order = Order.new(order_params)
# #product = basket.find(params[:product_id])
basket.each do |item_id|
#order.order_products.build(product: Product.find(item_id))
end
if #order.save
if #order.purchase(basket)
render "show"
else
render "failure"
end
else
render "new"
end
end
Can anyone lend a hand, please??
Many Thanks
Easy peasy!
This is a simple matter of control flow. In Ruby, as in most languages, exceptions interrupt the normal program flow. As your code is written now, #purchase is raising an exception when it fails.
That's fine and a perfectly valid design decision. But the code interacting with #purchase is this:
if #order.purchase(basket)
render "show"
else
render "failure"
end
That code has no exception handling, so any exception will be caught by Rails, program flow will halt, and you'll get either a detailed error page (in development mode) or a generic 500 error page (in production mode).
Since you profess to be new to Ruby and Rails, a little code substitution might make this clearer:
# If #purchase is successful, it evaluates to true.
if true
render "show" # 'show' view is rendered as expected. Flow stops.
else
render "failure"
end
# If #purchase fails, it raises an exception.
if raise StandardError, response.message
# ^^^ Exception is raised, flow stops here.
render "show" # This code is never reached.
else # This code is never reached.
render "failure" # This code is never reached.
end
As I implied in the beginning, though, it's an easy fix once you know what the issue is. You can simply handle the exception with rescue. Where you currently have an if/else block, you can swap in an if block and a rescue block:
if #order.purchase(basket)
render 'show'
end
rescue => e
render 'failure'
There's room for improvement here depending on your needs—since you're raising and rescuing StandardError, for example, your can't easily distinguish between a network failure and a declined card—but it'll get you moving again.
After a lot of fiddling and help, the working solution was to search for an error key within the response params hash and if an error was present add the message to the object errors. Not particularly elegant but it now does what i want.
ORDER MODEL
def purchase(basket)
response = GATEWAY.purchase(Product.total_basket_price(basket)*100, credit_card, purchase_options)
if response.params.key?('error')
self.errors.add :base, response.message
false
else
true
end
end
ORDERS CONTROLLER
Also switched the order of the if statements in the controller so that def purchase(basket) runs first before the order is saved, allowing the error message(s) from the response to be caught and displayed.
if #order.purchase(basket)
if #order.save
render "show"
else
render "new"
end
else
render "new"
end
VIEW
<%= if #order.errors.any?
#order.errors[:base].to_sentence
end%>

rescue_from Koala exceptions

Beginner question perhaps:
I'm trying to check my user permissions from facebook with Koala. In some cases I'm going to get thrown an error. So I just want to catch it and redirect to re-authenticate.
def check_facebook_permissions
if token = current_user.try(:authentications).find_by_provider('facebook').try(:token)
graph = Koala::Facebook::API.new(token)
permissions = graph.get_connections('me','permissions')
session[:facebook] = {}
session[:facebook][:ask_publish_actions] = true if permissions[0]['publish_actions'] != true && permissions[0]['publish_stream'] != true
end
rescue_from Koala::Facebook::APIError
# Do something funky here
end
I thought this was straightforward, but I'm never hitting my rescue. Instead I get:
Koala::Facebook::APIError (OAuthException: Error validating access token: Session has expired at unix time 1324026000. The current unix time is 1324352685.):
What am I missing here?
rescue_from is not a syntactic construct of Ruby like rescue is - it is a normal function, and you need a block to go with it. In your code, no code is given, rescue_from gets executed and effectively skipped - what is after it has no bearing on any exceptions raised before it (just as if you put any other function, like puts, instead of rescue_from).
See an example of rescue_from use here.
To make this code work, you need the vanilla Ruby rescue:
rescue Koala::Facebook::APIError => e
The correct syntax for handling errors in Ruby is:
begin
# do something that will throw an error
rescue StandardError => e # StandardError is the root class of most errors
# rescue the error
end

Resources