Pagefactory initialization - java

I implemented a framework with POM and Page Factory approach. I have a baseTest class with lots of init e.g: (every other test class extends it)
Registration regPage = PageFactory.initelements(driver,Registration.class);
Login loginPage = PageFactory.initelements(driver,login.class);
Details detailsPage = PageFactory.initelements(driver,details.class);
.. (more than 10)
It is working fine but I would like to find more elegant/structured way to handle it.
I tried the build it into the constructor:
public Registeration(WebDriver driver) {
super(driver);
PageFactory.initelements(driver,Registration.class(or can be this));
}
It this case, I got a huge heapmemory error however it would be very nice because I could use an assert to validate every pageObject in the constructor using title or whatever element on the page, right?
How can I structure my inits and how can I handle it with PageFactory using constructor?
Thanks!

Its because initElements stuck in infinite loop. PageFactory's InitElement function looks first for page's constructor with webdriver argument. Its like Page Creation call by your runner --> initElements (2nd line)--> Page Constructor called by initElements and this keeps circling around. So, you cannot initiate the page class within constructor of page using pagefactory. You may structure something like this
public class MyTest{
#Test
public void test(){
TestFactory.invokeBrowser();
MyPage page = TestFactory.getPage(MyPage.class);
}
}
//TestFactory class
public class TestFactory{
private static WebDriver driver;
public static void invokeBrowser(){
WebDriver driver = new ChromeDriver();
//invokes the browser
this.driver = driver;
}
public static <T> T getPage(Class<T> page){
return PageFactory.initElements(driver,page);
}
}

Related

TestNG Parallel execution always fails when i run more than one testcase, Among all only one testscript was passing

I am using Testng for parallel execution of my web testcase. Totally i am having 5 classes.
BaseClass - for initializing and closing of my browser
Core class - Mediator for all drivers initialized
Reusable methods - Click, settext, gettext... [extends Step #2 Core class, so driver comes from there only]
Page Object Class - To store all locators like name,ID,xpath.Uses all those reusable methods like click, gettext,settext.
Main Test Class.
Base Class
public class TestNGBase {
ThreadLocal<WebDriver> localdriver = new ThreadLocal<>();
#BeforeMethod
public void initialize(){
System.setProperty("webdriver.chrome.driver","C:\\SeleniumTest\\chromedriver.exe");
localdriver.set(new ChromeDriver());
}
public WebDriver driver(){
Core.setDriver(localdriver.get());
return localdriver.get();
}
#AfterMethod
public void teardown(){
localdriver.get().close();
localdriver.remove();
}
}
Core Class:
public class Core {
protected static WebDriver driver;
public static void setDriver(WebDriver driverr) {
driver = driverr;
}
}
Reusable Class:
public class WebMethods extends Core {
public WebMethods() {
}
public static void Click(By by) {
driver.findElement(by).click();
}
PageObject Class
public class pagemethods(){
By login = By.name("login");
public void login(){
WebMethods.click(login);}
}
MainTestclass1 : Will use above Pageobject
MainTestclass2 : Will use above Pageobject
MainTestclass3 : Will use above Pageobject
So in above 3 testcase when i trigger all those using testng.xml file. 3 new browser gets initialized and it successfully opens the url. But when i start using the all those reusable methods such as click(). Out of 3 Testcase, any of the two testcase is always getting failed.
I think problem starts Core class as it receives all drivers at the same time. It's collapsing something.
Can some one help me to solve this parallel execution failure problem.
Thanks
Try to not make the main class static. Create a class that makes an instance of the class and then executes. When you make a static class, the method is hanging off of that class, not an instance.
E.g.
public WebDriver
{
WebDriver myWebDriver = new WebDriver();
myWebDriver.whateverMethod();
}
When using threads avoid static. Try that first.

When trying to run Selenium Cucumber test in two browsers, test runs in only one browser

I am stuck whilst trying to run a selenium cucumber java script in two browsers (Chrome, Firefox). The test works fine when I use a single browser. I use selenium PageFactory class to initialise the web elements.
The problem is when I run the test for two browsers, the first browser opens, navigates to the URL and then nothing happens. Web elements are not initialised. The test moves on to the second browser, navigates to URL, web elements are initialised, subsequent test methods (testMethod1) run as expected. Why is the test not running on the first browser?
This is the PageFactory BasePage class holding the web elements:
public class BasePage {
private final WebDriver driver;
public BasePage(WebDriver driver) {this.driver = driver;} //constructor
#FindBy(id = "cc-amount")
public WebElement amountField;
This is the test class and how I have tried to run the test in two browsers:
public class Convert {
private static WebDriver driver;
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
private static int browser;
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
if (browser == 1) {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
} else if (browser == 2) {
WebDriverManager.firefoxdriver().setup();
driver = new FirefoxDriver();
}
driver.get(baseUtil.getMyUrl()); //Url coming from a utility class
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
basePage = PageFactory.initElements(driver, BasePage.class); //initialisation of the BasePage class conttaining the pagefactory web elements
After the browser initialisation, this method runs ok in the second browser (Firefox) but did not run at all in the first browser (Chrome):
public void testMetod1(String amount) throws InterruptedException {
basePage.amountField.click();
My suspicion is that PageFacory could not be initiated for both browsers in a single run but I do not know how to diagnose this further. It could also be a for loop error.
I believe it's a synchronisation issue. Just you need to create a class for each browser, then you can create another base class which contains all the shared code.
Creating a class for each browser will give you some advantages:
Cleaner code.
Your tests will run in parallel.
Specific test cases for a specific browsers (Specially for IE).
Solve this sync issue.
So basically instead of using Converter, you can create:
ChromeConverter
FirefoxConverter
And inside your TestExecuter class, just create instance for each one and run your tests.
And to be more detailed, you are using one class Convert and inside it you have two static variables:
private static WebDriver driver;
private static BasePage basePage;
Removing the static keyword from BasePage class won't solve the root cause as you are using BasePage which is a static class and can't be instantiated as instance class and it's inside an external library so you need to separate the classes as kind of wrapper.
Here you are trying to use the same exact initialisation with Chrome then Firefox which won't work in parallel or even in for loop until you can wait to the first test then dispose it and initialise all the stuff related to the second test again.
So the correct approach is to create a separate Convert for each browser like that:
ChromeConverter
FirefoxConverter
And In your TestExecuter you just need to initialise each one and use it, something like that:
#Given("...")
public void navigateToUrl(String url) throws InterruptedException {
chromeConverterApp.navigateToUrl(url);
firefoxConverterApp.navigateToUrl(url);
}
#When("^..")
public void enterCurrencies(String amount, String from, String to) throws InterruptedException {
chromeConverterApp.enterCurrencies(amount, from, to);
firefoxConverterApp.enterCurrencies(amount,from,to);
}
The for loop opens Chrome on the first run (browser=1), then opens Firefox on the second run (browser=2) and then continues with the test on the last opened browser (Firefox).
If you wish to run the complete test on both browsers, you need to put the rest of the test inside of the for loop.
Basically, you have something like:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
} //end of navigateToUrl
You need to change it to:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
testMethod1()
} //end of navigateToUrl
Your approach is not correct, if you need to run your tests in parallel you should go for either Selenium Grid or consider implementing other multi-threading option, i.e.:
Plain Java: ExecutorService
JUnit or TestNG: Maven Surefire Plugin Fork Options and Parallel Test Execution
In any case you will need to refactor your test to follow Parallel Tests - Best Practices at least:
Remove static modifier from the WebDriver declaration
Consider putting your WebDriver instance into the Thread Local Storage otherwise you will have clashes

How to pass selenium webdriver instance to another class

I browsed through the site but did not find the answer I am looking.
I have
Superbase class- here I just create object of a webdriver
Baseclass- In this class I extend Superbase class, invoke the driver, and open the URL.
Clicklink class- In this class, I again extend the Superbase Class but only to find a null pointer exception. I think I am getting the exception as the driver object is not initialized.
I am just a beginner, and have not tried the browserfactory and other options, as I want to start with simple flow.
Superclass
Public class Superclass
{
public webdriver Driver;
}
Baseclass
public class Baseclass extends Superclass
{
setting capabilities and launching the browser
}
ClickLink
public class Clicklink extends Superclass
{
here I want to click on a link
driver.findelement(by.xpath("xpath").click());
// after this statement I get a null pointer exception
}
Can you please guide me here? how can I achieve the same.
Thanks much!
SuperClass and BaseClass are very poor names. Do not use the language of coding to name your classes use the language of the problem. In this case, web site application testing, use LoginPage, CartPage, ProfilePage, etc. Use the Page Object Pattern.
I suggest you use the Factory Pattern to provide the instances of WebDriver for each test. Since all those fit the idea is a page, use class extension from a standard PageObject to provide this capability. When navigating, have the current page construct an instance of the new page and pass it the current webDriver connection instance. Then any manipulations you apply to that PageObject will be automatically applied to that webDriver instance and its associated browser instance. You should also use a PageFactory to provide instances of the pageObject.
public abstract class PageObject {
public WebDriver driver;
PageObject() {
// Page can initialise its self
this.driver = BrowserFactory.webDriver();
}
PageObject(final WebDriver webDriver) {
this.driver = webDriver;
}
}
This is lot of guesswork done from my side, but please make sure, that your Superclass actually sets the driver and returns it. You can actually make it in both methods:
public class Superclass
{
public WebDriver driver;
public Superclass(){
driver = new FirefoxDriver();
}
public WebDriver getdriver(){
if (driver == null){
driver = new FirefoxDriver();
return driver;
}else{
return driver;
}
}
}
And later in methods you call it by:
public class Clicklink extends Superclass
{
getdriver().findelement(by.xpath("xpath").click());
}
If you doesn't want pass driver instance to Page Objects constructor you could create some container class for driver and put it before test and remove it after run. For example:
class Driver {
public static ThreadLocal<IWebDriver> driverInstance = new ThreadLocal<IWebDriver>();
public static IWebDriver GetDriver() {
return driverInstance.Value;
}
public static void SetDriver(IWebDriver driver) {
driverInstance.Value = driver;
}
}
and make this container field ThreadLocal to avoid problems with parallel run.
I have taken a slightly different approach than most on this thread. When I start a test session, I pass the browser name as an argument (i.e. -Dbrowser=chrome) in order to be able to test my web application with different browsers. Then I used the "browser" system property to obtain the browser name when setup() is called by my test framework. In my case, I use JUnit annotations in order to JUnit to setup all needed dependencies prior to running any tests.
#BeforeClass
public static void setup() throws Exception {
// Set up other stuff
String browser = System.getProperty("browser");
try {
SessionDataProvider.driver = TestUtils.createDriver(browser);
} catch (Exception e) {
...
}
}
The createDriver(String) is a factory method that instantiates the correct driver.
public static WebDriver createDriver(String browserName) throws Exception {
WebDriver driver = null;
try {
switch(browserName) {
case "firefox":
// code to system props and instantiate the driver
break;
case "chrome":
// code to system props and instantiate the driver
break;
case "ibrowser":
// code to system props and instantiate the driver
break;
case "edge":
// code to system props and instantiate the driver
break;
case "safari":
// code to system props and instantiate the driver
break;
default:
throw new Exception("Unsupported browser: " + browserName);
}
return driver;
}
Then, when I execute a step definition, I simply obtain the driver from the data provider class:
#And("(I click)/Click on {string}")
public void click(String arg) {
// Parse String arg and create web element locator...
try {
By locator = ...;
WebElement element = new WebDriverWait(SessionDataProvider.driver, 2)
.until(ExpectedConditions.elementToBeClickable(locator));
element.click();
} catch (Exception e) {
// handle exception
}
}
I did use below code in utility class like below
public static WebDriver setup(WebDriver driver)
{
if(driver == null) {
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
return driver;
}else
return driver;
//System.out.println("in method "+ driver.getTitle() );
}

Multiple browser windows opening automatically in cucumber

I don't know why I am getting 2 firefox browsers opened for the follwoing example. Can some one please tell me what is wrong in below code. I am new to cucumber and I am trying to develop cucumber poc with page object model.
Feature file:
Scenario: Smoke test for application
Given I am on home page
Step Defination file:
public class HomePageSteps {
CustomerDetails customerDetails;
HomePage homePage=new HomePage();
public HomePageSteps(CustomerDetails customerDetails){
this.customerDetails=customerDetails;
}
#Before
public void environmentSteup(){
homePage.envSetup();
}
#Given("^I am on home page$")
public void i_am_on_home_page() throws Throwable {
homePage.openURL();
}
}
Actual implementation of Step definition file:(HomePage.java)
public class HomePage extends BasePage{
public void openURL() {
driver.get("https://applicationURL.aspx");
System.out.println("I am on home page executed");
}
public void envSetup() {
driver=new FirefoxDriver();
driver.manage().window().maximize();
}
}
BasePage.java
public abstract class BasePage {
protected WebDriver driver=new FirefoxDriver();
}
CustomerDetails.java
public class CustomerDetails {
private String mdn=null;
private String Fname=null;
private String Lname=null;
public String getMdn() {
return mdn;
}
public void setMdn(String mdn) {
this.mdn = mdn;
}
}
2 firefox browsers are opened:
First it opens a blank browser. Later it opens another browser and in this browser it opens the application URL.
You have two calls to open browser windows...
Once in the sub-class in envSetup() - driver=new FirefoxDriver();
And in the super class driver variable declaration with initialization - protected WebDriver driver=new FirefoxDriver();
You have to remove one of them, no need for the super class one... This is the one giving you the blank window
Refer to this page. Your maximize() call in envSetup() might be doing more than you think
In selenium webdriver what is manage() [driver.manage()]
edit:
You also do not need to instantiate a new FirefoxDriver() outside of BasePage as you have already instantiated a driver field with that object. Anything extending BasePage will have access to that driver field. It is not a problem that you're doing this, it is just extraneous code that doesn't need to be there

Page Class using PageFactory

I am new to PageFactory and referring this tutorial https://www.toptal.com/selenium/test-automation-in-selenium-using-page-object-model-and-page-factory
An example from this page:
public class HomePage {
private WebDriver driver;
//Page URL
private static String PAGE_URL="https://www.toptal.com";
//Locators
//Apply as Developer Button
#FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER")
private WebElement developerApplyButton;
//Constructor
public HomePage(WebDriver driver){
this.driver=driver;
driver.get(PAGE_URL);
//Initialise Elements
PageFactory.initElements(driver, this);
}
public void clickOnDeveloperApplyButton(){
developerApplyButton.click();
}
}
Why create a private instance of WebDriver? It is appearing with yellow line for me.
When to use how = HOW and when we can straight away use xpath= //id..?
In constructor we are again passing WebDriver parameters?
The yellow line is because you are not using the driver variable declared with the class but the one you are passing as the variable to the constructor. Try using :
this.driver.get(PAGE_URL);
//Initialise Elements
PageFactory.initElements(this.driver, this);
You can use xpath = "//div" straight away when you are not using how.LINK_TEXT as you also have to provide the text for searching the link if you want to locate an element via LINK_TEXT.
In the constructor, you are passing the driver instance so when the page object is created, it has a driver instance as it is not the page instantiating the driver.

Categories