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.
Related
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
Each of our page objects has around 80-100 PageObject elements defined. This is a byproduct of a very data centric application and I don't see that changing any time soon. We have many text fields, select lists, buttons, and menus. I want to clean up these somehow and put them somewhere, but the things I've thought about don't seem very clean.
Ideas:
Move elements for each PageObject into a corresponding module and include that module into a single PageObject only
create a YAML config file, with the mapping of element_type => element_name => element_id and then call the corresponding element methods using metaprogramming
Utilize the upcoming PageObject sections feature to create different "panels" within each page and then include those Panels into the page.
The first solution has the problem that each module can be included in other classes if somebody doesn't pay attention. It's not restricted to the single class
The second solution seems slow and not very "rubular", along with being confusing to update elements for those new to the project
The third solution seems like the best, but like the first solution, you could potentially include Sections into a class that they don't belong in.
What other way can I move the definition of these elements out of the class so that our class is cleaner, shorter, easier to read, and defined to Ruby Specifications (i.e. the Ruby Style Guide). They really just seem like config data rather than ruby code, so I feel like they don't belong in the class. Especially when we have 80+ elements in each class.
I don't see why you wouldn't keep the elements in the page object class. That's kind of the point of the class, right? If they're shared across multiple pages, then I understand putting them in a module and mixing in. But why abstract the unique elements on that page away from the page class?
But if you're absolutely going to do this, I'd think this is the best option:
Move elements for each PageObject into a corresponding module and include that module into a single PageObject only
There are ways you could ensure these modules are included only in the classes you expect. I have Module#included in mind. Something like:
module MyPageElements
def self.included cls
raise "Expected to be included on MyPage, got: #{cls}" unless cls == MyPage
end
# ... elements ...
end
create a YAML config file, with the mapping of element_type => element_name => element_id and then call the corresponding element methods using metaprogramming
Seems like this would be a headache to bring new, unfamiliar people up to speed on the code. Ultimately, I agree that it's just too un-Ruby like.
Utilize the upcoming PageObject sections feature to create different "panels" within each page and then include those Panels into the page.
Since I've just barely looked at this (thanks for linking me to the PR), I don't feel like I can speak to its efficacy.
I've stumbled upon the following piece of code in an Rspec test and I must say I more or less figured out what it does but I can't find relevant sources to prove it. Please point me to a gem or docs that describe:
describe SomeModule::Salesforce::Lead do
before do
SomeModule::Salesforce::Lead.any_instance.expects(:materialize)
end
...
end
It seems that for :each example in this spec it sets expectation on any instance of the class described above to receive a call to :materialize method AND it actually redefines the method to do nothing . The last part seems crucial because it avoids connecting to SalesForce in test environment but I can't find confirmation for this.
any_instance is documented under Working with Legacy code
You are correct in that it both sets an expectation and stubs out the original method on any given instance of a class.
Previous versions of RSpec accomplish this by monkeypatch the ruby core classes (Object and BaseObject)
RSpec 3 has a new syntax which does not rely on monkeypatching:
before do
expect_any_instance_of(SomeModule::Salesforce).to receive(:materialize)
end
Ok I've just found that I was looking in wrong sources, it doesn't come from RSpec but from Mocha Mock (expects and any_instance) http://gofreerange.com/mocha/docs/Mocha/Mock.html#expects-instance_method
Thanks #tomasz-pajor #https://stackoverflow.com/users/2928259/tomasz-pajor
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 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.