I am new to automation and I have started using Watir Webdriver to automate a website. However, there are certain pieces of code which can be reused for multiple test cases. How can I group the reusable pieces of code into a method which I can call in every test case ? Could you please provide references or examples ?
You are interested in methods.
Here is an example:
def automation_snippet
#browser.text_field(:id => 'field_id').set 'foo'
end
Later you will probably be interested in classes, page objects, and modules.
This is basic ruby stuff. You will need to make sure that whatever method you want to use is in scope, too.
So, keywords to start with are methods and scope. If you are using Cucumber, then you can define methods in any step definition file and they will be available in all your other Cucumber tests.
Related
I am using the PageObject pattern, which abstracts HTML details away from the top-level of the test. I am doing this using the SitePrism gem.
In my example, I have a home page (HomePage.rb):
class HomePage < SitePrism::Page
set_url ENV['BASE_URL']
section :header, HeaderSection, "div[class='headerSection']"
which refers to a common page section, HeaderSection (HeaderSection.rb):
class HeaderSection < SitePrism::Section
element :sign_in_button, "a[class='signIn']"
and a step definition for my tests (login.rb):
And(/^I am not logged in/) do
#home_page = HomePage.new # actually, this is specified in env.rb
expect(#home_page.header).to have_sign_in_button
end
Instead of exposing the web element to the step definition, I want to encapsulate this within a class method of HomePage. It seems that the best way to do that is to put the assertion into a class method within HomePage itself (HomePage.rb):
def amILoggedIn
expect(header).to have_sign_in_button
end
The above assumes that I am also using include RSpec::Matchers.
My method would then be the only thing exposed to the step definition (login.rb):
And(/^I am not logged in/) do
#home_page.amILoggedIn
end
As part of SitePrism, the sign_in_button element has its own built-in methods to check for its presence, which is:
header.has_sign_in_button?
Question
In terms of best practice, which is the most recommended way of writing this assertion within the amILoggedIn method (even though it appears they both use the same instruction),
expect(header).to have_sign_in_button
or
header.has_sign_in_button?
expect(header).to have_sign_in_button and header.has_sign_in_button? do two different things. The first is an assertion (raises an exception if it fails) and the second just returns a boolean response. If what you want is an assertion you could assert on the boolean response assert header.has_sign_in_button? or expect(header.has_sign_in_button?).to be true but the failure message from have_sign_in_button is going to be a lot more descriptive.
Old question but providing updated answer
Given SitePrism now uses implicit waiting by default as Thomas has said you've got two different method signatures, and two different outputs.
Also dependent on who you speak to, it is considered poor practice to include the matchers in a variety of places. Using Cucumber with SitePrism gives you a nice separation of concerns to perform the testing in the Cucumber World (step_definitions), and isolate all other modelling concerns to the support code.
Using SitePrism as described by the official docs will allow you (when using cucumber), to access all rspec methods in the cucumber world, and test easily and effectively.
I'm writing a new rspec test case using Capybara (SitePrism actually, which uses Capybara) and I've run into an apparently known issue: https://github.com/jnicklas/capybara/issues/1396. Essentially, due to a change in one or the other, RSpec and Capybara now both have methods named all, and when I try to use SitePrism to find a group of elements or SitePrism sections, Capybara invokes the wrong method and returns something of the type RSpec::Matchers::BuiltIn::All rather than the expected array of Capybara or SitePrism objects.
For some reason, all my old tests, including many with very similar usage of sections and elements constructs, work perfectly fine. I'm having a really hard time finding differences between them that would account for one failing and the other succeeding. I briefly tried rolling back either Capybara or RSpec to just try to make the problem go away for the moment, but it seemed silly, trying to pinpoint when the problem was introduced, when existing test cases that have been running every day never broke.
Can anyone advise me on why one works and the other fails? Here is what these two test cases have in common:
Both spec files require the same spec_helper.rb file.
The spec_helper.rb file has both require rspec and require capybara.
Each spec file uses require_relative to require a page object used in each test.
Each page object file has a sections :table_rows, <SECTION CLASS>, <ROW CSS> declaration. They may have different names, classes, and CSS Selectors, but they're the same basic construct.
In each spec file, methods are invoked on the page objects that reference table_rows.
Referencing table_rows in one of these older test cases works just fine, but I'm getting the name collision error in the new test case. Anyone know why that might be, so I can fix the new test case?
Failing that, does anyone know how I can separate things so that the page object requires capybara but not rspec and the spec file requires rspec but not capybara to prevent the collision? I don't know much about Ruby package management, but it seems like in order to run an rspec test case that uses a page object, it's all going to be mixed together. I'm not sure of a way to avoid requiring them both.
Failing that, does anyone know which versions of either of these two packages I could theoretically use to avoid the issue? I did experiments with rspec 3.2 and capybara 2.4, and neither seemed to work, and I gave up there, because I remembered that no matter how far back in time I go, the test cases I've already written were working fine, and it just seemed silly to try to solve it this way.
Require capybara after Rspec or always call page.all rather than just all
Not the best approach, but you can monkey patch the SitePrism::Page class. Here you replace 'all' by 'page.all':
SitePrism::Page.class_eval do
def find_all(*find_args)
page.all(*find_args)
end
end
I am working through Learn Ruby The Hard Way and came across something intriguing in exercise 49.
In parser.rb I have a function named skip(word_list, word_type) at the top level, which is used to skip through unrequited words (such as stop words) in user input. It is not encapsulated in a class or module. As per the exercise I have to write a unit test for the parser.
This is my code for the Unit Tests:
require "./lib/ex48/parser"
require "minitest/autorun"
class TestGame < Minitest::Test
def test_skip()
word_list = [['stop', 'from'], ['stop', 'the'], ['noun', 'west']]
assert_equal(skip(word_list, 'stop'), nil)
assert_equal(skip([['noun', 'bear'], ['verb', 'eat'], ['noun', 'honey']], 'noun'), nil)
end
end
However, when I run rake test TESTOPTS="-v" from the command line, these particular tests are skipped. This seems to be because there is a clash with the skip method in the Minitest module because they run perfectly after I change the name to skip_words.
Can someone please explain what is going on here exactly?
"Top level functions" are actually methods too, in particular they are private instance methods on Object (there's some funkiness around the main object but that's not important here)
However minitest's Test class also has a skip method and since the individual tests are instance methods on a subclass of Test you end up calling that skip instead.
There's not a very simple way of dealing with this - unlike some languages there is no easy way of saying that you want to call a particular superclass' implementation of something
Other than renaming your method, you'll have to pick an alternative way of calling it eg:
Object.new.send(:skip, list, type)
Object.instance_method(:skip).bind(self).call(list, type)
Of course you can wrap this in a helper method for your test or even redefine skip for this particular Test subclass (although that might lead to some head scratching the day someone tries to call minitest's skip.
I am looking for a general way to maintain a separation of core logic and logging/debugging/terminal output code in a Ruby application's codebase. Ideally, I would like to have a separate "tracer" codebase parallel to that of my application core (the 'lib' directory in a typical Ruby project). The tracer code would live in a special directory (perhaps called "trace") in the same way that unit tests often live in a parallel structure in the test/spec directory. Tracer files would extend target classes with wrappers for various methods. These wrappers would do things like writing to a log, setting a breakpoint (e.g. with pry's' binding.pry) or incrementing a progress bar whenever the method returned. The loading of the tracer code could then be controlled by a single switch.
I have done some research and come up mostly blank. I've found some pieces of the functionality I'm after; for example, the standard library's Tracer class and the method_decorators gem. But I am wondering there is a more complete solution out there, something analogous to rspec for testing. Does such a thing exist? Or are there perhaps other ways of dealing with this problem?
If you, for instance, are looking at enabling this code only in development, then you can add an initializer in rails: /config/initializers
if( Rails.env.development?)
require "logging_wrappers"
end
In your lib/ folder, you can add your extensions into lib/logging_wrappers.rb
To override the find_by_id function on your User model, simply add the following:
class User
alias_method :unlogged__find_by_id, :find_by_id
def find_by_id(id)
Rails.logger.info "About to find by id"
x = unlogged__find_by_id(id)
if (x.blank?)
Rails.logger.info "We didn't find any users"
end
end
For more information on this technique, several good examples can be found at: http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/
I've been looking for some tutorials on creating ruby gems, but all seem to be overly complicated. Essentially, all I want is to make a gem that (trivial example alert) allows the user to place a method add_one(x) in their code, once the gem is installed.
A lot of tutorials explain how you can call the gem's methods with Classname.method(), but that's really not what I want. RyanB has a good example here, but again I can't get it to work - mainly because of the 3 lines of code at the bottom of uniquify.rb that read.
class ActiveRecord::Base
include Uniquify
end
In my case, my main module is called AddOne, so how would I go about doing this? Again, sorry for the trivial example, I'm just looking to get started so that my gems will allow the user to call plain ol' methods, without specifying Modules or Classes. In Ryan's example, he's able to simple call uniquify() in the code.
In the example given, he can call uniquify as a method because he is calling it inside a class which includes ActiveRecord::Base. (He calls uniquify inside the class Product < ActiveRecord::Base...end block) There isn't any real magic here.
To my understanding, what you desire is easily achievable, but isn't a good idea, 'best practice' considered. You can define anything you want in your gem. For instance, I could package a file called say_hello.rb inside a gem called salutations.
say_hello.rb
def say_hello
puts "Hello everybody"
end
By requiring the packaged gem salutations I would gain access to anything defined therein. However, it might lead to unexpected consequences. Do what you need to do, but just understand that it might not be the best idea. Hence, you build things into classes or modules to contain them, and to prevent conflicts with other code. Good luck.