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);
}
然后在LoginTest
class中,构造完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;
}
这是一个演示项目,实现是使用页面对象模式和数据驱动框架完成的。
在继承之下,使用了构造函数概念。
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);
}
然后在LoginTest
class中,构造完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;
}