1

This is a demo project, implementation is done using Page Object Pattern and Data driven framework.

Below inheritance, constructor concepts are used.

The config.prpoerties file has username, password, url and browser.

Base Class

package com.crm.qa.base;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;

import com.crm.qa.utilities.TestUtil;

public class TestBase {
    
    public static Properties prop;
    public static WebDriver driver;
    
    public TestBase() {
    
        //import variables from Config.properties file
        try {
            prop=new Properties();
    
            FileInputStream fis=new FileInputStream(System.getProperty("user.dir")+"/src/main/java/com/crm/qa/configuration/config.properties");
    
                prop.load(fis);         
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }   
    
    public static void  initialization()
    {
        String browser= prop.getProperty("browser");
        
        if(browser.equalsIgnoreCase("firefox"))
        {
            System.setProperty("webdriver.gecko.driver", "./Drivers/geckodriver.exe");
            FirefoxOptions firefoxOptions = new FirefoxOptions();
            firefoxOptions.setCapability("marionette", true);
            driver = new FirefoxDriver(firefoxOptions);     
        }

        if(browser.equalsIgnoreCase("chrome"))
        {
            System.setProperty("webdriver.chrome.driver", "./Drivers/chromedriver.exe");
            driver=new ChromeDriver();
        }
    
        driver.manage().window().maximize();
        driver.manage().deleteAllCookies();
        driver.manage().timeouts().pageLoadTimeout(TestUtil.PAGE_LOAD_TIMEOUT, TimeUnit.SECONDS); 
        driver.manage().timeouts().pageLoadTimeout(TestUtil.IMPLICIT_WAIT, TimeUnit.SECONDS); 
        driver.get(prop.getProperty("baseURL"));
    }               
}

Login Page Object Class

package com.crm.qa.pages;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import com.crm.qa.base.TestBase;

public class LoginPageCRM extends TestBase {
    
    
    //PageFactory
    @FindBy(name="username")
    WebElement username;
    
    
    @FindBy(name="password")
    WebElement password;
    
    @FindBy(xpath = "//input[@type='submit']")
    WebElement loginBtn;    
    
    @FindBy(xpath= "//a[contains(text(),'Sign Up')]")
    WebElement signUP;
    
    @FindBy(xpath="//img[contains(@class,'img-responsive')]")
    WebElement crmLogo;
    
    //to initialize page objects
    
    public LoginPageCRM()
    {
        PageFactory.initElements(driver, LoginPageCRM.class );
    }
    
    
    //Actions
    //1. verify title of page
    public String validateLoginPageTitle()
    {
        return driver.getTitle();
        
    }
    
    //2. Validate crm image
    public boolean validateCRMLogo()
    {
        return crmLogo.isDisplayed();
    }
    
    //3. Login
    public HomePageCRM login(String uname, String pwd)  
    {
        username.sendKeys(uname);
        password.sendKeys(pwd);
        loginBtn.click();
        
        return new HomePageCRM();
    }

}

<------------------------------------LoginTest Class------------------------------------->

package com.crm.qa.testcases;

import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.crm.qa.base.TestBase;
import com.crm.qa.pages.HomePageCRM;
import com.crm.qa.pages.LoginPageCRM;


public class LoginTest extends TestBase{
    
    public LoginTest()
    {
        super(); 
    }
    
    LoginPageCRM loginPage;
    public HomePageCRM homePageCRM;
    
    @BeforeTest
    public void setUP()
    {
    initialization();
    loginPage=new LoginPageCRM();
    }
    
    
    @Test(priority=1)
    public void LoginPageTitleTest()
    {
        String pageTitle= loginPage.validateLoginPageTitle();
        Assert.assertEquals(pageTitle,"CRMPRO  - CRM software for customer relationship management, sales, and support.");
        
    }
    
    @Test(priority=2)
    public void CRMLogoTest()
    {
        Assert.assertEquals(loginPage.validateCRMLogo(), true);
    }
    
    @Test(priority=3)
    public  void loginPageTest()
    {
        homePageCRM= loginPage.login(prop.getProperty("username"), prop.getProperty("password"));
    }
    
    
    @AfterTest
    public void tearDown()
    {
    driver.quit();      
    }

}

Now when I run the LoginTest class as TestNG Test, I get error in console as below-->

com.crm.qa.testcases.LoginTest#setUP


Exception

java.lang.StackOverflowError
    at java.base/java.util.stream.StreamOpFlag.fromCharacteristics(StreamOpFlag.java:733)
    at java.base/java.util.stream.StreamSupport.stream(StreamSupport.java:70)
    at java.base/java.util.Arrays.stream(Arrays.java:5446)
    at java.base/java.util.Arrays.stream(Arrays.java:5427)
    at java.base/java.lang.Class.methodToString(Class.java:3579)
    at java.base/java.lang.Class.getConstructor0(Class.java:3508)
    at java.base/java.lang.Class.getConstructor(Class.java:2244)
    at org.openqa.selenium.support.PageFactory.instantiatePage(PageFactory.java:128)
    at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:64)
    at com.crm.qa.pages.LoginPageCRM.<init>(LoginPageCRM.java:33)
    at jdk.internal.reflect.GeneratedConstructorAccessor9.newInstance(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
    at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128)
    at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:350)
    at java.base/java.lang.Class.newInstance(Class.java:645)
    at org.openqa.selenium.support.PageFactory.instantiatePage(PageFactory.java:131)
    at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:64)
    at com.crm.qa.pages.LoginPageCRM.<init>(LoginPageCRM.java:33)
    at jdk.internal.reflect.GeneratedConstructorAccessor9.newInstance(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
    at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128)
    at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:350)
    at java.base/java.lang.Class.newInstance(Class.java:645)
    at org.openqa.selenium.support.PageFactory.instantiatePage(PageFactory.java:131)
    at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:64)
    at com.crm.qa.pages.LoginPageCRM.<init>(LoginPageCRM.java:33)
    at jdk.internal.reflect.GeneratedConstructorAccessor9.newInstance(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)

 

 ........

My Eclipse verion : Version: 2021-03 (4.19.0) Build id: 20210312-0638

eclipse.ini file

-startup
plugins/org.eclipse.equinox.launcher_1.6.100.v20201223-0822.jar
--launcher.library
C:\Users\JACOB\.p2\pool\plugins\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.100.v20210209-1541
-product
org.eclipse.epp.package.java.product
-showsplash
C:\Users\JACOB\.p2\pool\plugins\org.eclipse.epp.package.common_4.19.0.20210311-1200
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vm
C:/Program Files/Java/jdk-15.0.1/bin/javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=11
-Dosgi.instance.area.default=@user.home/eclipse-workspace
-Dsun.java.command=Eclipse
-XX:+UseG1GC
-XX:+UseStringDeduplication
--add-modules=ALL-SYSTEM
-Dosgi.requiredJavaVersion=11
-Dosgi.dataAreaRequiresExplicitInit=true
-Dorg.eclipse.swt.graphics.Resource.reportNonDisposed=true
-Xms256m
-Xmx2048m
--add-modules=ALL-SYSTEM
-Declipse.p2.max.threads=10
-Doomph.update.url=http://download.eclipse.org/oomph/updates/milestone/latest
-Doomph.redirection.index.redirection=index:/->http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/
Gautham M
  • 4,816
  • 3
  • 15
  • 37
Vidhya
  • 79
  • 2
  • 6

3 Answers3

2

I encounterd this bug once. How to fix it, change

PageFactory.initElements(driver, LoginPageCRM.class );

to

PageFactory.initElements(driver, this);
lucas-nguyen-17
  • 5,516
  • 2
  • 9
  • 20
  • Explanation to why the current code `PageFactory.initElements(driver, LoginPageCRM.class)` fails -> `initElements` tries to create a object of `LoginPageCRM` by invoking it's constructor. As the constructor of `LoginPageCRM` contains `PageFactory.initElements(driver, LoginPageCRM.class)`. So this process is repeated infinite times leading to Stackoverflow error. This is the root cause of the issue. – Gautham M Jul 04 '21 at 06:10
  • So PageFactory.initElements(driver, this) will return object of the current class? – Vidhya Jul 04 '21 at 08:25
0

PageFactory.initElements(driver, LoginPageCRM.class) is causing the StackOverflowError because initElements tries to create a object of LoginPageCRM by invoking it's constructor. And as the constructor of LoginPageCRM contains PageFactory.initElements(driver, LoginPageCRM.class), this process is repeated infinite times leading to StackOverflowError. This is the root cause of the issue.

Since I am not much familiar with selenium, I assume @lucasnguyen17's answer explains how to fix it. But I would suggest a few changes before passing this to that method within a constructor.


NOTE: The below mentioned issue might NOT cause an issue in your case as there are no other operations with this inside the constructor. But the below can be followed as a general guideline/best-practice


An object is considered not properly constructed, if the this reference "escapes" during construction. i.e the object is said to be properly constructed only if the constructor is fully executed. If you fix your code as below, you are passing a not properly constructed object to the initElements method. This could be thought as analogous to renting a house to someone before the construction is even completed. Hence this is a wrong practice and should NOT be done as it could lead to unexpected results as you may not know what the method you are calling, is going to do with the object you pass.

public LoginPageCRM()
{
    // The object is not fully constructed at this point
    PageFactory.initElements(driver, this);
}

So I would suggest the below refactor:

In LoginPageCRM just create a default constructor without any code within it. (Java would actually create one, if there are no other constructors in the class). Then create a method that would do the PageFactory.initElements(driver, this):

public LoginPageCRM()
{
}

public void init() {
    PageFactory.initElements(driver, this);
}

Then in the LoginTest class, after constructing the LoginPageCRM object, invoke the new init method, so that you can ensure that you are passing a properly constructed object.

@BeforeTest
public void setUP() {
    initialization();
    loginPage = new LoginPageCRM();
    loginPage.init();
}
Gautham M
  • 4,816
  • 3
  • 15
  • 37
0
public LoginPageCRM()
{
    PageFactory.initElements(driver, LoginPageCRM.class );
}
    

This line in your page class constructor in turn call below internal PageFactory method to instantiate LoginPageCRM class which again call this LoginPageCRM constructor. It continue till stack overflows.

  **T page = instantiatePage(driver, pageClassToProxy);**


        public static <T> T initElements(WebDriver driver, Class<T> pageClassToProxy) {
        T page = instantiatePage(driver, pageClassToProxy);
        initElements(driver, page);
        return page;
      }
Vikas Piprade
  • 272
  • 2
  • 7