Does the Selenium FindBy instantiate WebElement instances in a PageObject class? - selenium

Does the Selenium FindBy annotation actually instantiate WebElement instances, and if so, what is the connotation to the framework that uses them?
What I have been doing in my page objects looks like this right now. All my test framework methods take By locators as arguments (not WebElement instances).
//fields in the page
public static final By SEARCH_BOX = By.id("input-what");
My question is, does using FindBy instantiate WebElement instances at the time of class instantiation? If so, then I suspect my framework methods would need to take WebElement instances. Is this correct, and which is preferred in a framework: By locator arguments or WebElement arguments?
#FindBy(id = "input-what")
public WebElement SEARCH_BOX_ELEMENT;
I know I am expecting a somewhat opinionated answer. If you give me a hyperlink citation for your belief, then I think that would be a reasonable answer. I think this has far-reaching implications: for example, the methods in Selenide framework, don't take WebElement instances in their arguments and therefore PageObjects that use FindBy annotations would not be able to pass those elements to Selenide methods?

If I understand what you are asking, your answer is on the very first line of the documentation:
In order to use the PageFactory, first declare some fields on a PageObject that are WebElements or List<WebElement>...
Or possibly the end of the same documentation:
WebElements are evaluated lazily. That is, if you never use a WebElement field in a PageObject, there will never be a call to "findElement" for it.
Edit:
To complete the answer: PageObject is a coding style of collecting information about your webapp. In order to make this coding style less verbose, Selenium offers a PageFactory, which uses the #FindBy annotation to decorate WebElements in your PageObject classes.

I was not aware of these new annotations in selenium.
Kind of have seen page object pattern implemented through Stand Alone Weld based frameworks.
In any case, I think it's relatively clear:
https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/support/FindBy.html
Used in conjunction with PageFactory this allows users to quickly and easily create PageObjects.
Wenever you ask the factory to return you a page object, it will inject the page object with the webelements are read at that point in time.
If you page has something like comet functionality, and is going to get stale with time, you would be well advised to not expect your injected web elements to get stale.
From what I've seen so far, you want page objects to simplify your interaction with the browser content. But you want them to be stateless.
I would defnitly, as a rule of thumb, of ajax enabled pages, never have state on my page objects.
If Wanted to write to a text box.
I Would offer an api:
public void writePassword(String password){
... callPrivateGetterToGetPasswordField().writeText(password);
}
And so on. I would promote methods that use short burst of read dom content and write dom content and try to not have page objects harvesting stale content.

Related

Selenium documentation

I can see from other online sources that the getAttribute method is part of the WebElement interface,I could find this information on other sites but nothing regarding this interface and method in the Selenium documentation. Is this interface no longer supported? What is the best practice if this interface is no longer supported?
The documentation for WebElement interface and specifically the method getAttribute is rather extensive:
See the javadoc

What is the use of getWrappedElement() method which is declared in WrapsElement interface and why does only Select class implement it?

I am able to understand all the methods implemented in Select class except getWrappedElement() method. Can anyone please help me out on this?
This is used if you have your WebElement encapsulated in some other entity. There are two examples:
org.openqa.selenium.support.ui.Select that actually mimics the behavior of select control. It implements WrapsElement in order to provide the actual element it "decorates" (however it is not a decorator hence I put quotes here).
org.openqa.selenium.support.events.EventFiringWebDriver.EventFiringWebElement which works in pair with EventFiringWebDriver. It actually wraps the base element and allow to run actions before and after your element methods call. Hence you might want to obtain that base WebElement in some reason which is supported by implementation of WrapsElement
In the latter case there is also a code that "consumes" entities implementing WrapsElement. Such objects are expected for example in executeScript in order to unwrap objects you're passing as parameters for your script.
WrappedElement is also used in a number of internal Selenium classes where it tests if an element is the instance of WrappedElement and unwraps it before proceed to next steps.

What is a Selenium wrapper?

Does it wrap around Selenium and provide a simpler or different method of invoking the functionality of Selenium?
I looked it up on Google and the best information I could find was this one https://www.ontestautomation.com/using-wrapper-methods-for-better-error-handling-in-selenium/.
This doesn't explicitly explain what a Selenium wrapper is but gives enough information to help understand what it is.
One of the definitions of a "wrapper" is:
In the context of software engineering, a wrapper is defined as an entity that encapsulates and hides the underlying complexity of another entity by means of well-defined interfaces.
So, any custom code you might use that implements Selenium code could be understood as a wrapper.
For example, Katalon Studio is a testing tool that uses Selenium under the hood i.e. Katalon's WebUI class methods are a wrapper around Selenium methods. The following two pieces of code are equivalent - they do the same thing:
Selenium (and Java)
WebElement element = driver.findElement(By.cssSelector("css-selector-of-the-element"));
element.click();
Katalon
WebUI.click(testObject) //testObject defined elsewhere
This is just a simple example, but it shows how can you hide complexity behind simpler commands.
I know this question has already been answered but I can see it was never accepted as an answer. Now, the answer above explains exactly what a wrapper is : encapsulation. Which in itself means also that it hides the underlying complexity of another entity (Selenium classes in this case).
But let me elaborate and give you an actual example.
I've built a Framework around Selenium and made a nuget package out of it internal to my company. But this is one example on how to wrap Selenium By class. Using a delegate, you can simplify a lot of the calling methods :
private delegate void ValidationMethodDelegate(By locator, int timeout = ELEM_TIMEOUT);
//This method actions a delegate for regularly used methods with a By locator parameter,
//the value of the selector and the selector type which is a built-in framework enum
private void ActionMethod(ValidationMethodDelegate delegateMethod, string selectorValue, SelectorType selectorType)
{
if (!string.IsNullOrWhiteSpace(selectorValue))
{
switch (selectorType)
{
case SelectorType.Id:
delegateMethod(By.Id(selectorValue));
break;
case SelectorType.Xpath:
delegateMethod(By.XPath(selectorValue));
break;
case SelectorType.TagName:
delegateMethod(By.TagName(selectorValue));
break;
case SelectorType.CssSelector:
delegateMethod(By.CssSelector(selectorValue));
break;
case SelectorType.ClassName:
delegateMethod(By.ClassName(selectorValue));
break;
case SelectorType.LinkText:
delegateMethod(By.LinkText(selectorValue));
break;
case SelectorType.PartialLinkText:
delegateMethod(By.PartialLinkText(selectorValue));
break;
default:
break;
}
}
else
{
throw new AssertionException($"{this.GetType().Name}::{MethodBase.GetCurrentMethod().Name}():: Selector Value : '{selectorValue}' cannot be null or empty.");
}
}
//Example on how the delegate is used
public void Click(string selectorValue, SelectorType selectorType)
{
ActionMethod(PageHelper.Click, selectorValue, selectorType);
}
The PageHelper is a static class that implements internally to the framework most of Selenium's methods with assertions and waiting implementations. I have several layers of complexity in my framework. But you can make it simple too. The method click for me is wrapped also in another class that implements two methods one that finds the element by and the other than waits for an element to appear on screen. Both are other wrappers around Selenium methods and assertions.
If you are only doing tests for one application and won't have further use of Selenium then a framework is not a solution for you. Also wrappers would be kind of redundant outside your test solution.
I would say that wrappers would only be useful in the context where you are making multiple use for it (like the click or find element, etc.)
"Wrapper" is more like a software development design pattern, which developers use in the codebase when it is necessary.
You can read more in the book:
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=sr_1_2?dchild=1&keywords=software+development+patterns&qid=1605187094&sr=8-2
In scope of automated testing, there are other terms. I will explain for mobile automation.
Driver (Espresso, UIAutomator, Robotium, XCUITest) - receive commands from test and send them to app specialized interface in understandable way
You sent a command to press a button to the GUI driver - it accepts it via API and sends to the app (and we see a tap on the button in GUI).
Another app (which is over driver, let's call it superstructure in this context) that interacts with the app under test via one or more drivers (increasing usability or increasing possibilities) like Appium, Calabash.
Frameworks (JUnit, TestNG, Cucumber) - app that allows us to prepare, launch and gather all info regarding test executions
It will look like this:
Framework -> Our tests -> Superstructure -> Driver -> GUI in our application

Creating PageObjects in WebDriverIO

I've been creating PageObjects for WebDriverIO and have been following the ES6 method for Page Object pattern in the WebDriverIO documentation.
However, someone on my team suggested simply making objects of selectors, and then calling those strings in the tests. Is there a good reason why the Page Object pattern returns elements and not string of the selectors?
Page Object returns elements instead of just the selector string to allow actions to be called directly on the elements e.g.
PageObject.Element.waitForDisplayed()
Instead of you doing
Browser.waitForDisplayed(PageObject.Element)
Which may get lengthy and doesn't chain as well. You can find more actions that can be performed on elements here
However, you can also get the string of the selector if you want by doing
PageObject.Element.selector()
Chaining e.g.
PageObject.Element.waitForDisplayed().click()
I think the point is allow you to use the objects directly. So:
MyPageObject.MyElement.click()
versus:
browser.click(MyPageObject.MyElement)
Just a little less verbose

How (and whether) to modify the object in a Chai BDD chain

I am using ChaiJS with my Casper-Chai plugin, and I am not sure how to go about a particular issue I am encountering.
I would like to be able to write tests such as:
expect(casper).selector("#waldo").to.be.visible;
This is pretty straightforward, with a call such as
utils.addChainableMethod(chai.Assertion.prototype, 'selector',
selectorMethod, selectorChainableMethod);
utils.addMethod(chai.Assertion.prototype, 'visible', visibleMethod);
Where the *Method references are to functions that perform respective tests or chainable calls.
My question is what is the best way to have e.g. 'selector' modify descendants in the chain. Two options come to mind:
use utils.flag(chai, 'object') to change it to the selector; or
create a new flag with e.g. utils.flag(chai, 'casper-selector')
When 'visible' is called, it can read the respective flag. Where modifying the 'object' seems to be useful is when calling e.g. 'length' later-on. However, I am concerned somewhat about unexpected side-effects of changing the 'object'.
I may also want to modify the object, for 'length' tests, down the chain, such as:
// there can be only one!
expect(casper).selector("#waldo").length(1)
// but that one has 4 classes
expect(casper).selector("#waldo").class.to.have.length(4)
Grateful for any thoughts and input.
---- EDIT ----
Okay, so here is the conceptual challenge that gave root to Casper-Chai, that requires a bit of a description of what Casper is and why Casper-Chai should be a Chai plugin and not just an alternative to the existing Casper API. Casper is a wrapper around the PhantomJS headless web browser, and as such Casper runs two effectively two distinct virtual machines: A
"Controller" and a headless web browser.
There is no DOM or "document" or "window" object in the controller; the Controller is in this respect a lot like Node.js, albeit using the WebKit javascript parser. In parallel, PhantomJS starts a headless web browser. The Controller can then communicate through a PhantomJS/Casper API to the headless browser. The Controller can tell the headless browser what pages to load, what javascript to run (which is like typing javascript into the console), and even mimic events such as keyboard input and mouse clicks. The headless browser has a full DOM and javascript stack: it is a web-page loaded in WebKit. You can capture screenshots of what WebKit would render.
Casper-Chai is run in the Controller. Tests created in Mocha + Chai in the Controller are meant to evaluate against the state of the headless browser. Although we can copy state from the browser to the Controller and run tests on that copied state, my limited experimentation with that design revealed problems inherent to the design (i.e. efficiency, race conditions, performance, and potential side-effects). The problem is that the browser state is dynamic, complex, and can be unwieldy.
So taking John's example, expect(casper.find("#waldo")).to.be.visible would not work, since there is no DOM, unless the object returned by Casper did some sort of lazy evaluation/mediation. Even if we serialized and copied the HTML element there there is no CSS in the Controller. Then, even if there were CSS for #waldo, there is no way to test the hierarchy as if it were a web-browser. We would have to copy a substantial portion of the DOM and all the CSS and then replicate a web-browser to test whether #waldo is visible - for every single test. Casper-Chai is meant to avoid this problem by running the tests in the browser.
Just for a little extra illumination, a trivial comparison is getting the number of elements that match a selector. One could write expect(casper.evaluate(function () {return __utils__.findAll('.my_class')}).to.have.length(4), where casper.evaluate runs the given function in the headless browser and returns a list of DOM elements matching the selector as strings; and you can think of __utils__ as Casper's version of jQuery. Alternatively, one could write expect(casper).selector('.my_class').to.have.length(4) where selector becomes the 'object' and it has a getter .length that calls 'casper.evaluate(function () { return utils.findAll('.my_class').length`. Only the integer length is returned. For a short number of tests either works fine, but for larger numbers of tests this performance characteristic becomes impactful (here, in this simplistic form, and potentially to a substantially greater extent in more complex cases).
One could of course write expect(casper.evaluate(function () { __utils__.findAll('.my_class').length }).equal(4), but if one is going to write tests like this, why bother with BDD/Chai? It eliminates the benefit of readability that Chai offers.
It is also worth noting that there may be multiple Casper instances in the Controller, corresponding to multiple PhantomJS pages. Think of them as spooky tabs.
So given Domenic's answer that modifying the 'object' flag is the appropriate way to go about it, this seems the most practical way - subject to any thoughts in light of the above description.
I hope the above describes why Casper-Chai should be a plugin and not just an API extension to Casper. I'll also run this by Casper's author to see if he has any input.
It may not be a perfect relationship, but I am hopeful that Casper & Chai can get along handily. :)
The difficulty stems from the fact that casper has a highly procedural API, with methods like Casper#click(String selector) and Casper#fetchText(String selector). To fit naturally with chai, an object-oriented API is needed, e.g. Casper#find(String selector) (returning a CasperSelection object), CasperSelection#click(), CasperSelection#text(), etc.
Therefore I suggest you extend the casper object itself with a find or selector method which returns an object on which you can base your assertions. Then you will not need to change the object flag.
expect(casper.find("#waldo")).to.be.visible;
expect(casper.find("#waldo")).to.have.length(1)
expect(casper.find("#waldo").class).to.have.length(4)
I mostly agree with #John. You're performing your expectations on some other object, so saying
expect(casper).select("#waldo").to.have.length(1)
is very strange. You're not expecting anything about casper, you're expecting something about casper.find("#waldo"). Consider also the should syntax:
casper.should.select("#waldo").have.length(1)
// vs.
casper.find("#waldo").should.have.length(1)
That said, if you're dead set on this kind of API, this is exactly what the object flag is for. Chai even does this, to make assertions like
myObj.should.have.property("foo").that.equals("bar")
work well:
https://github.com/chaijs/chai/blob/49a465517331308695c3d8262cdad42c3ac591ef/lib/chai/core/assertions.js#L773