java.lang.StackOverflowError 当 运行 class 在 Selenium 中作为 TestNG 测试

java.lang.StackOverflowError when running class as TestNG Test in Selenium

这是一个演示项目,实现是使用页面对象模式和数据驱动框架完成的。

在继承之下,使用了构造函数概念。

config.prpoerties 文件有用户名、密码、url 和浏览器。

基地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"));
    }               
}

登录页面对象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();
    }

}

<------------------------------------登录测试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();      
    }

}

现在,当我 运行 LoginTest class 作为 TestNG 测试时,我在控制台中收到如下错误消息 -->

com.crm.qa.testcases.LoginTest#setUP


Exception

java.lang.WhosebugError
    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)

 

 ........

我的 Eclipse 版本: 版本:2021-03 (4.19.0) 构建 ID:20210312-0638

eclipse.ini 文件

-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/

这个bug我遇到过一次。如何解决,改

PageFactory.initElements(driver, LoginPageCRM.class );

PageFactory.initElements(driver, this);

PageFactory.initElements(driver, LoginPageCRM.class) 导致 WhosebugError 因为 initElements 试图通过调用它的构造函数来创建 LoginPageCRM 的对象。由于 LoginPageCRM 的构造函数包含 PageFactory.initElements(driver, LoginPageCRM.class),此过程重复无限次导致 WhosebugError。这是问题的根本原因。

由于我对 selenium 不是很熟悉,我假设 @lucasnguyen17 的回答解释了如何修复它。但我建议在将 this 传递给构造函数中的该方法之前进行一些更改。


注意:下面提到的问题可能不会导致您的情况出现问题,因为在构造函数中没有其他使用 this 的操作。但是下面的可以作为一般的guideline/best-practice


如果 this 引用在构造过程中“逃逸”,则对象被视为未正确构造。也就是说,只有当构造函数被完全执行时,对象才被正确构造。如果您按如下方式修复代码,则您将 未正确构造的对象 传递给 initElements 方法。这可以被认为类似于在施工完成之前就把房子租给某人。因此,这是一种错误的做法,应该 NOT 这样做,因为它可能会导致意外结果,因为您可能不知道您正在调用的方法将如何处理您传递的对象。

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

所以我建议进行以下重构:

LoginPageCRM 中,只需创建一个默认构造函数,其中不包含任何代码。 (如果 class 中没有其他构造函数,Java 实际上会创建一个)。然后创建一个方法来执行 PageFactory.initElements(driver, this):

public LoginPageCRM()
{
}

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

然后在LoginTestclass中,构造完LoginPageCRM对象后,调用新的init方法,这样就可以保证传递的是正确的构造对象。

@BeforeTest
public void setUP() {
    initialization();
    loginPage = new LoginPageCRM();
    loginPage.init();
}
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;
      }