I would like a way to take an error generated within a specific test method inside a Test::Unit::TestCase and turn it into a failure with a more friendly generic message. I keep thinking this should be possible with some inheritance but I can't quite get my head around it.
class CalenderTest001 < Test::Unit::TestCase
def testZoneCal001
Fixture.reset
$driver = Selenium::WebDriver.for :firefox
$driver.get "http://myTestSite.com/"
$driver.find_element(:id, "IDthrowsAnError").click
end
end
The effect I would like is to have the entire thing wrapped in a begin rescue end block with the rescue block looking something like this.
rescue Selenium::WebDriver::Error::NoSuchElementError => e
#mark this test as a failure not an error
You can use the assert_nothing_raised construct:
def testZoneCal001
assert_nothing_raised "Something went wrong!" do
Fixture.reset
$driver = Selenium::WebDriver.for :firefox
$driver.get "http://myTestSite.com/"
$driver.find_element(:id, "IDthrowsAnError").click
end
end
Related
im building a small game in ruby to practice programming, so far everything has went well but im trying to implement multiplayer support, i can connect to the server and i can send information but when I try to read form the server it just freezes and my screen goes completely black. and i cant find the cause, ive read the documentation for the gem im using for TCP and i dont know, maybe i missed something, but if any of you have some insight I would really appreciate it
heres the repo if this code isnt enough
https://github.com/jaypitti/ruby-2d-gosu-game
heres the client side code
class Client
include Celluloid::IO
def initialize(server, port)
begin
#socket = TCPSocket.new(server, port)
rescue
$error_message = "Cannot find game server."
end
end
def send_message(message)
#socket.write(message) if #socket
end
def read_message
#socket.readpartial(4096) if #socket
end
end
heres the gameserver
require 'celluloid/autostart'
require 'celluloid/io'
class Server
include Celluloid::IO
finalizer :shutdown
def initialize(host, port)
puts "Starting Server on #{host}:#{port}."
#server = TCPServer.new(host, port)
#objects = Hash.new
#players = Hash.new
async.run
end
def shutdown
#server.close if #server
end
def run
loop { async.handle_connection #server.accept }
end
def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("\n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
#players[user] = message[1..9] unless #players[user]
#objects[message[1]] = message[1..9]
when 'del'
#objects.delete message[1]
end
end
response = String.new
#objects.each_value do |obj|
(response << obj.join("|") << "\n") if obj
end
socket.write response
end
rescue Exception => exception
puts exception.backtrace
end
end # end data
end # end loop
rescue EOFError => err
player = #players[user]
puts "#{player[3]} has left"
#objects.delete player[0]
#players.delete user
socket.close
end
end
server, port = ARGV[0] || "0.0.0.0", ARGV[1] || 1234
supervisor = Server.supervise(server, port.to_i)
trap("INT") do
supervisor.terminate
exit
end
sleep
it just freezes and my screen goes completely black. and i cant find the cause
A good trick you can look at is attaching to your process with either rbspy or rbtrace to see that is going on when it is stuck.
You can also try first reducing dependencies here a bit and doing this with a simple threadpool prior to going full async with celluloid or event machine.
First of all you should not be rescuing Exception all over the place. Wrapping long begin rescue blocks around nested iterators is begging for trouble.
It sounds like a threading issues, memory and/or CPU but that's just a guess. Try to monitor your resources or use some performance checking gems. But for the love of Satoshi Nakamoto, please write some test coverage and see your methods fail miserably, then fix them!
Some of these may help:
group :development do
gem 'bullet', require: false
gem 'flamegraph', require: false
gem 'memory_profiler', require: false
gem 'rack-mini-profiler', require: false
gem 'seed_dump'
gem 'stackprof', require: false
gem 'traceroute', require: false
end
I'm writing Selenium tests, using Watir-Webdriver and RSpec, which can be a bit spotty when they're first being developed. I've run into a situation where I want to create something on the UI in before :all, however it can throw exceptions (based on timing or poor loading). When that happens I want to take a screenshot.
Here's what I have:
RSpec.configure do |config|
config.before(:all) do |group| #ExampleGroup
#browser = Watir::Browser.new $BROWSER
begin
yield #Fails on yield, there is no block
rescue StandardError => e
Utilities.create_screenshot(#browser)
raise(e)
end
end
end
I run it and get an error:
LocalJumpError: no block given (yield)
The reason I assumed yielding would work is RSpec's definition of before:
def before(*args, &block)
hooks.register :append, :before, *args, &block
end
How can I wrap the code I've put in my before :all in a begin/rescue block without having to do it on every suite?
Thanks in advanced.
The code you've written in the before hook is the &block you're referring to in RSpec::Hooks#before. The hook yields to your code, then runs your tests after the yield is complete.
As for how to make this work, I think this should do:
RSpec.configure do |config|
# ensures tests are run in order
config.order = 'defined'
# initiates Watir::Browser before all tests
config.before(:all) do
#browser = Watir::Browser.new $BROWSER
end
# executes Utilities.create_screenshot if an exception is raised by RSpec
# and the test is tagged with the :first metadata
config.around(:each) do |example|
example.run
Utilities.create_screenshot(#browser) if example.exception && example.metadata[:first]
end
end
This configuration requires the first test be tagged with metadata:
describe Thing, :first do
it "does something" do
# ...
end
end
This way, you'll only take a screenshot at the beginning of your run for a failing test, and not after every failing test. If you'd rather not mess with metadata (or prefer your tests are run in random order), you could do something like this:
RSpec.configure do |config|
# initiates Watir::Browser before all tests
config.before(:all) do
#test_count = 0
#browser = Watir::Browser.new $BROWSER
end
# executes Utilities.create_screenshot if an exception is raised by RSpec
# and the test is the first to run
config.around(:each) do |example|
#test_count += 1
example.run
Utilities.create_screenshot(#browser) if example.exception && #test_count == 1
end
end
This works for me. Instead of begin/rescue in the before :all hook,
config.after :each do
example_exceptions = []
RSpec.world.example_groups.each do |example_group|
example_group.examples.each do |example|
example_exceptions << !example.exception.nil?
end
end
has_exceptions = example_exceptions.any? {|exception| exception}
#Handle if anything has exceptions
end
My code has a lot of this: driver.blahblahblah. Consider the following code sample, taken from http://www.browserstack.com/automate/ruby.
require 'rubygems'
require 'selenium-webdriver'
driver = Selenium::WebDriver.for(:remote,
:url => "http://USERNAME:ACCESS_KEY#hub.browserstack.com/wd/hub")
driver.navigate.to "http://www.google.com/ncr"
element = driver.find_element(:name, 'q')
element.submit
puts driver.title
driver.quit
How can I make driver implicit? For example there's a method called driver.save_screenshot(). I want to say save_screenshot("a.png") because only the driver variable/object has this method.
You can use delegate from ActiveSupport like the example below:
require 'active_support/core_ext/module/delegation'
class MyClass
delegate :find_element, :save_screenshot, to: :driver
def foo
find_element
save_screenshot
end
def driver
#driver ||= Driver.new
end
end
class Driver
def find_element
puts "find_element"
end
def save_screenshot
puts "save_screenshot"
end
end
MyClass.new.foo
Or decorate the driver using SimpleDelegator (but I don't recommend that).
If you have lots of methods whose receiver is driver, then a way to make the receivers implicit is:
driver.instance_eval do
method_1...
method_2...
...
end
but notice that this slows down a little. If you are just finding a way to be lazy, then the best way is to make the local variable as short as a single letter, and do not bother making it implicit.
d = .... # instead of `drive`
d.method_1...
d.method_2...
...
hi getting error which is not understand able i am new to ruby so please help .
i checked all thing which is possible for me.
require 'rubygems'
require 'selenium-webdriver'
require 'test/unit'
class SeleniumTest < Test::Unit::TestCase
driver = Selenium::WebDriver.for :firefox
driver.get "http://localhost:9000/assets/build/index.html#/login"
element = driver.find_element :name => "email"
element.send_keys "kaushik#abc.com"
element = driver.find_element :name => "password"
element.send_keys "password"
element.submit
page.find(:xpath, "//a[#href='#/courses/new']").click
#click_link ("//a[#href='#/courses/new']")
puts "Page title is #{driver.title}"
#page.should have_selector(:link_or_button, ' Create New Course...')
wait = Selenium::WebDriver::Wait.new(:timeout => 2000)
driver.quit
end
getting This error:-
TestClass.rb:7:in `<class:SeleniumTest>': undefined local variable or method `logger' for SeleniumTest:Class (NameError)
from TestClass.rb:6:in `<main>'
It seems that you didn't included the complete source code.
Besides that, all you code is bare in class SeleniumTest. You should put your code into the appropriate method or methods.
This type of errors are generated when objects or methods are not created or not scoped well.
In your case, the error message is telling you that the object logger in line 7 of you script does not exist.
As I can see from your source code, line 7 falls into the class definition. I guess you have something like
logger.log 'logging text'
in that line but you delete it from your post, and in lines 4 and 5 you have something like:
require 'logger'
logger = Logger.new('file.log')
If that is the case, you could put logger = Logger.new('file.log') inside the class definition, or define an instance object of type Logger inside the SeleniumClass class, or a global method or something other for logging messages.
Examples:
class SeleniumTest < Test::Unit::TestCase
logger = Logger.new('file.log')
logger.log "logging text"
...
end
or
class SeleniumTest < Test::Unit::TestCase
def initialize
#logger = Logger.new('file.log')
end
def log(message)
#logger.log mesage
end
...
def some_method_with_your_code
...
log "logging text"
...
end
end
st = SeleniumTest.new
st.some_method_with_your_code
... or something similar...
I hope this can help you solve your problem.
If not, you should put the complete source code and tell us what are you trying to do!
I am using the Selenium Webdriver libraries in Ruby. A typical piece of code looks like this:
require 'rubygems'
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
# driver is an instance of Selenium::WebDriver::Driver
url = 'http://www.google.com/'
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
driver.get(url)
wait.until { driver.title.start_with? "Google" }
I would like to create a subclass of Selenium::WebDriver::Driver called Selenium::WebDriver::Driver::MyClass that will contain some new methods and instance variables.
As the above code illustrates, the way that instances of Selenium::WebDriver::Driver are created is with Selenium::WebDriver.for.
Without wholesale copying of code, how can I create a version of Selenium::WebDriver.for that does the same thing as Selenium::WebDriver.for but creates instances of Selenium::WebDriver::Driver::MyClass?
Why not just override the Selenium::WebDriver.for ? let me show you that my an example
# selenium code
module Selenium
class WebDriver
def self.for
puts "creating oldclass"
end
end
end
# your code
class Selenium::WebDriver
def self.for
puts "creating myclass"
end
end
Selenium::WebDriver.for
output:
creating myclass
Safe alternative is to derive class from Selenium::WebDriver and use that in your code, or to the extreme you can just open Driver class and add your behavior to it.
Check the source code. Selenium::WebDriver.for simply delegate the method call to Selenium::WebDriver::Driver.for.
If you don't have listener attached, you can simple create your own bridge MyClass::Bridge.new and then pass that to Selenium::WebDriver::Driver.new.
If you insist override the for method, here is some code snippet that might help.
module Selenium
module WebDriver
class Driver
class << self
alias_method :old_for, :for
def for(browser, opts = {})
if browser == :myclass
# create your MyClass::Bridge instance and pass that to new()
else
old_for(browser, opts)
end
end
end
end
end
end
If you just want to define some extra methods on your driver, you do not need to override WebDriver.for.
The following worked well for me:
First, in file customdriver.rb
require 'selenium-webdriver'
class CustomDriver < Selenium::WebDriver::Driver
#a custom method..
def click_on (_id)
element = find_element :id => _id
element.click
end
#add other custom methods here
#....
end
Then, in file main.rb
require-relative 'customdriver'
driver = CustomDriver.for :chrome
driver.click_on("buttonID")
Regards,