Multiple projects in POP - how to handle inheritance? - java

I have a problem how handle inheritance in my test project using Page Object Pattern. In our company we have one stable version of our web appliaction - something called Demo from which we copy ready modules. Kind like a base to other new versions of clients applications. They are in 90% all the same. So i think it would be great to make Base Page Objects Classes and in many projects inheritance from that classes.Sadly they are different in this 10% and I need to override xpaths in class fields. I have a problem when i inheritance from Base POP Class, make a field in child class, and method from parent class(Base POP Class) run on field in parent class not on child. Let show me your example. This is may parent class:
public class BaseHomePage {
#FindBy(xpath = "exampleXpath")
protected WebElement profileButton;
public BaseHomePage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void goToProfile() {
profileButton.click();
}
and this is my child class:
public class HomePage extends BaseHomePage {
#FindBy(xpath = "otherXpath")
protected WebElement profileButton;
public HomePage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver,this);
}
public void goToProfile() {
profileButton.click();
}
}
So problem is I need to use the same method goToProfile() in child class, because when i not method from parent call on field in parent class. I don't want to duplicate code and i search for answers but i'm not sure this is good use of POP in multiple projects. What i need to make it properly? Or maybe u would suggest me make it in another way? Thanks for help!

Related

How to handle inheritance on page objects

I have the following issue:
I have a home page where I load the URL with the following structure:
public class HomePage extends BasePage {
public HomePage(WebDriver driver) {
super(driver);
getDriver().get("URL");
}
Then I have another page, which extends from the HomePage:
public class OptionsPage extends HomePage {
public OptionsPage(WebDriver driver) {
super(driver);
}
Now, the issue is on the test, where I want to get the OptionsPage from an action (let's say clicking a button present on HomePage). It seems that the page just loads again once I call it:
// Open page
HomePage home = getHomePage(); //loads URL from BasePage
// click on some option which takes me to the OptionsPage
OptionsPage options = home.clickOnRandomOption(); //this method right here loads the URL again, from there the assertion fails since the page just loads again.
Assert.assertTrue(options.isRandomOptionSelected(), "Obviously it wasn't selected.");
The method to click the option on the home page looks like this:
public OptionsPage clickOnRandomOption() {
getWait().until(ExpectedConditions.elementToBeClickable(RandomOption));
RandomOption.click();
return new FlightOptionsPage(getDriver());
}
Can anyone help me out?
Your challenge is that when you instantiate your optionsPage you're executing all constructors up the chain.
From your code structure, you're running: OptionsPage -> HomePage -> BasePage every time you want to create an optionsPage. It's the testing POM equivalent of the Gorilla-banana-jungle problem.
I can see that the return type from home.clickOnRandomOption() is an OptionsPage so I'm assuming that's where you instantiate the options page.
Two suggestions - either of them will fix your problem, but I recommend you think about implementing both:
Don't chain your pages like that. Keep each one separate and ONLY extend once i.e. each page only extends from BasePage. If there is a common method which more than one page needs, put it in your BasePage and there's no need to extend page from other pages.
Don't execute test-context-impacting code in your constructors - use contructors only for initating variables. By this, I mean that when you create your HomePage do not navigate (driver.get("URL")) in the constructor. This will cause future complications for you when you run tests. For example, what if, you wanted to start on pageX and navigate to home via an auto-redirect then do more actions on home? - your POM won't support it as when you create homePage you're going to navigate the URL to there.

Code not executing when placed in separate test classes

I am wring a PageFactory framework for a website using maven+TestNG,
I have page wise PageObject classes where all web elements and actions specific to page are present like LoginPageObject, AccountSelectionPageObject...
I have a class "Base" where the common elements like WebDriver, Logger are present.
I have a class "BasePage" where the common actions like click, scroll, select, refresh... are present
MyTestng.xml is having separate <class> entry for both all individual pages.
It's just that I am initializing the browser object in #BeforeSuiit and stored/placed it in the Base class which is being extended in my Test classes
Below is the flow/arch I came up for my project.
Issue:
I have multiple #Test in each of my test classes.
When my Test classes are executed individually, all #Test script executed,
but when I execute them continuously, i.e. my testng file have separate entries for all my test classes, my execution fails. Error says unable to find element on page, I have wait statements, but still it's not working.
I have tried debugging code, but not able to find the reason as the flow stops on starting of second page with exception saying element not found
Code:
#FindBy(id="listAccounts")
WebElement accountDropdown;
public void selectAccount(){
logger.info("Selecting Account");
implicitwait(10);
Select dropdown = new Select(accountDropdown);
logger.info("Drop down is multiple::"+ dropdown.isMultiple());
}
Expected:
Code should execute completely even when separated code page wise.
Actual:
When I have all pages code in one test class, code executed.
But when I place them separately in page wise test class, element not found exception is thrown.
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"id","selector":"listAccounts"}
In POM using PageFactory framework, one should initialise PageFactory in Constructor of PageClasses. Please find below code snippet which might work in your case.
public class LoginPage extends TestBase {
public LoginPage() {
PageFactory.initElements(driver, this);//Here driver is initialised in TestBase class and inherited in LoginPage class
}
//your code
#FindBy(id="listAccounts")
WebElement accountDropdown;
public void selectAccount(){
logger.info("Selecting Account");
implicitwait(10);
Select dropdown = new Select(accountDropdown);
logger.info("Drop down is multiple::"+ dropdown.isMultiple());
}
}
A complement to the Krishna answer:
PageFactory.initElements(driver, this);
The code above can be moved to the base class and from the LoginPage, you just pass the webdriver on the constructor like this.
public class LoginPage extends Base {
public LoginPage(Webdriver driver) {
super(driver);
}
...
public class Base {
public Base(Webdriver driver) {
PageFactory.initElements(driver, this);
}
...

Extend Multiple Classes in Java while Retaining Functionality of each Class

I'm using WebDriver (Selenium) and I want to add custom methods to WebDriver such as driver.performCustomAction().
Being that I could instantiate an instance of FirefoxDriver or ChromeDriver I cannot simply extend FirefoxDriver bec I would not be able to use the functionality with Chrome Driver.
Tech I could create a new class and pass an instance of WebDriver to the constructor (so it could be either FF or Chrome) but then I would be unable to perform all of the non custom actions of each class such as findElements(), getText() on the new object.
In other words, if my new class is called WrappedWebDriver and I instaniate a new instance of it as follows:
WebDriver FFDriver = new FirefoxDriver();
WrappedWebDriver WDriver = new WrappedWebDriver(FFDriver);
I will be able to call WDriver.performCustomAction() but I will not be able to call WDriver.findElement() or any of the other methods defined in the FirefoxDriver class (or the actions I could perform using FFDriver ).
How can I add new methods that apply to both FirefoxDriver and ChromeDriver without writing it twice while retaining all functionality of each respective class?
P.S: I know Java doesn't allow multiple inheritance is there some other way around it?
I think you can create Wrapper class which will hold instance of the Webdriver and you will wrap methods of webdriver which u want to support. You can do it for instance like this:
public class WrappedWebDriver {
public WebDriver driver;
public WrappedWebDriver(WebDriver driver){
this.driver = driver
}
public WebElement find(By by){
//your customization code
return driver.findElement(by);
}
public void setText(By by, String text){
//your customization code
driver.findElement(by).sendKeys(text)
}
public void performCustomAction(){
//your customization code
}
}
You can customize Webdriver standard methods by adding some functionality in wrapped methods. By making driver public, you give user an option to choose beetween using standard driver methods or your customized methods.
There is nice and useful Wrapper API for selenium named Conductor. https://github.com/conductor-framework/conductor. There you can find more complex example how to wrap WebDriver.
You perhaps need to be Extending EventFiringWebDriver. This class by its very behavior is composite in nature (Its created by taking a reference to an existing webdriver instance) and it was originally designed to be used for tapping into the before/after events of all webdriver originating actions. But it can very well suite your purpose.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class WrappedDriver extends EventFiringWebDriver {
public WrappedDriver(WebDriver driver) {
super(driver);
}
public void performCustomAction(){
//your customization code
}
}
So in essence you first build a decorator that implements all the interfaces that an actual RemoteWebDriver sub-class would implement and then have your customized class extend the decorator. The decorator class here in this case is EventFiringWebDriver

Webelement, which is defined in page class, is not accessible in Test class

I am automating a website using POM framework and I have one page class and a test class (out of many).
The page class is : FindPeople.java and the code I have written inside is like below:
public class FindPeople {
#FindBy(id="ContentPlaceHolderDefault_Body_Body_Content_***SearchSimpleDialog_13_tbQuery")
WebElement serachfield;
#FindBy(xpath=".//*[#id='ContentPlaceHolderDefault_Body_Body_Content_***SearchSimpleResults_14_pnlResults']/div[1]/div/a")
WebElement serachresult;
public void typeInSearchField()
{
serachfield.sendKeys(DataProviderFactory.readHomeData().getPeopledata(2, 0));
}
}
I have test class called VerifyInputField.java and code inside is like below:
public class VerifyInputField {
WebDriver driver;
#Test
public void verifyInputField() throws AWTException {
driver= BrowserFactory.getBrowser("Chrome");
BrowserFactory.getURL();
FindPeople findpeople = PageFactory.initElements(driver, FindPeople.class);
findpeople.typeInSearchField();
}
}
The problem is, the Webelemnts (serachfield, serachresult) I identified in FindPeople.java; are not accessible in VerifyInputField.java. I mean, I am not getting the usual methods like click(), gettext() using any of these elements.
Am I doing anything wrong?
The properties serachfield and serachresult seems like package private to which are not accessible from outside of the package. All you need to do is make them public with public access modifier.
And, speaking about the best practices of POM, you should not access those properties publicly anyway. Try creating methods inside FindPeople class which will help you to use those properties and make the methods public
Every property which is declared without access modifier is private, so you have to specify it exactly, if you want to create public property.
But I agree with Saifur about best practices and creating inside method for access to properties.
May be some useful info here: https://stackoverflow.com/a/215505/2131257.

Whats the best way to initialize and use a WebDriver PageObject using PageFactory?

With WebDriver and PageFactory, using Java we are implementing a new automation project, and we've been experimenting with various ways of having PageObjects created. We're torn on a few different ideas, and want to make sure we don't work ourselves into a corner.
Is it best to, as documented in the WebDriver documentation, provide an initialized WebDriver to a PageFactory, along with the class template to create a new PageObject?
driver.get(URL);
PageObject page = PageFactory.initElements(driver, PageObject.class);
// elsewhere
class PageObject {
private WebDriver driver;
public PageObject(WebDriver driver) {
this.driver = driver;
this.validateUrl();
}
public void validateUrl() throws Exception {
if (!driver.getUrl().equals(url)) {
throw new Exception("URL not valid");
}
}
}
However, since the PageObject knows a lot about itself, such as perhaps its URL, can we not have the Page Object do the work?
PageObject page = new PageObject(driver);
page.goToUrl();
// elsewhere
class PageObject {
private WebDriver driver;
private String url;
public PageObject(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void goToUrl() {
driver.get(url);
}
}
I suppose I don't see much of an advantage to having the PageFactory do the instantiation versus just initialization, however I don't want to stray from the standards setup by the architects if there's a reason for it.
Thanks
One of the advantage of Page Factory:
Scenario:
In your application, you are having 100 fields in a page. The same page is called for 50 times.
If this type of scenario is done by using Page Object means, it will find each element again and again. There may be a chance for degradation of the performance.
If the same scenario is done by using Page Factory means, it will find the elements only for the first time, and then it will take from the cache. By this the performance is increased.
The best way is to let the frameworks like Geb and Thucydides abstract out the PageObjects and their initialization. I have been using Geb + Spock BDD combination for this and the results so far has been excellent.

Categories