从不同的 class 中实例化相同的 class
Instantiating the same class from different classes
在 BaseTest.java 中,我有一个方法可以初始化 AppiumDriver 类型的对象。
在 LogInTest.java 中,我正在创建 BaseTest class 的实例并调用 initializeDriver 方法。
再次在 LoginPage.java 中,我正在创建另一个 BaseTest class 实例,但我没有调用
初始化驱动方法。因为在 LoginPage.java 中我只创建了一个 BaseTest class 的实例而不调用 initializeDriver 方法,所以与 LoginPage.java 的 BaseTest 实例相关联的驱动程序对象不应该为空吗?
这个程序按照我老师的解释工作,但我只是不明白它是如何工作的 LoginPage.java
base.getDriver() 应该为空,因为我从未在那里调用过 initializeDriver 方法。
BaseTest.java
package com.qa;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.Properties;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import com.utils.TestUtils;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import io.appium.java_client.remote.MobileCapabilityType;
public class BaseTest {
public BaseTest() {
}
protected static AppiumDriver driver;
protected static Properties prop;
protected FileInputStream fis;
protected String dir;
public void setDriver(AppiumDriver driver) {
this.driver=driver;
}
public AppiumDriver getDriver() {
return driver;
}
public void initializeDriver(String platformName,String platformVersion, String deviceName) {
try {
dir=System.getProperty("user.dir");
fis=new FileInputStream(new File(dir+File.separator+"src"+File.separator+"main"+File.separator+"java"+File.separator+"com"+File.separator+"qa"+File.separator+"config.properties"));
prop=new Properties();
prop.load(fis);
DesiredCapabilities cap=new DesiredCapabilities();
cap.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName);
cap.setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion);
cap.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
cap.setCapability(MobileCapabilityType.AUTOMATION_NAME, prop.getProperty("UIAutomator2"));
//cap.setCapability(MobileCapabilityType.UDID, "50");
String appURL=dir+File.separator+prop.getProperty("appLocation");
cap.setCapability(MobileCapabilityType.APP, appURL); // if you want to install and launch a new app
cap.setCapability("autoGrantPermissions", true);
//cap.setCapability(MobileCapabilityType.BROWSER_NAME, "Chrome");
//cap.setCapability("chromedriverExecutable", "C:\chromedriver-mobile_browser\chromedriver.exe");
//cap.setCapability("newCommandTimeout", 900); //how long
cap.setCapability("appPackage", prop.getProperty("androidAppPackage"));
cap.setCapability("appActivity", prop.getProperty("androidAppActivity"));
cap.setCapability("autoAcceptAlerts", "true"); // to accept all alerts
//cap.setCapability("avdLaunchTimeout", "300000");
cap.setCapability("unlockType", "pin");
cap.setCapability("unlockKey", "74159");
URL url=new URL(prop.getProperty("appiumURL"));
driver=new AndroidDriver(url,cap);
String sessionID=driver.getSessionId().toString();
}
catch(Exception e) {
e.printStackTrace();
}
}
public void waitForVisibility(MobileElement e) {
WebDriverWait wait=new WebDriverWait(driver,TestUtils.WAIT);
wait.until(ExpectedConditions.visibilityOf(e));
}
public void click(MobileElement e) {
waitForVisibility(e);
e.click();
}
public void sendKeys(MobileElement e, String keys) {
waitForVisibility(e);
e.sendKeys(keys);
}
public String getAttribute(MobileElement e, String attribute) {
waitForVisibility(e);
return e.getAttribute(attribute);
}
public void quitDriver() {
driver.quit();
}
}
LogInTest.java
package com.qa.test;
import org.testng.annotations.Test;
import com.qa.BaseTest;
import com.qa.pages.LoginPage;
import com.qa.pages.ProductsPage;
import io.appium.java_client.AppiumDriver;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import java.lang.reflect.Method;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
public class LogInTest{
LoginPage logInPage;
ProductsPage productsPage;
BaseTest base;
AppiumDriver driver;
@Parameters({"platformName","platformVersion","deviceName"})
@BeforeClass
public void beforeClass(String platformName,String platformVersion,String deviceName) {
base=new BaseTest();
base.initializeDriver(platformName, platformVersion, deviceName);
}
@AfterClass
public void afterClass() {
base.quitDriver();
}
@BeforeMethod
public void beforeMethod(Method m) {
logInPage=new LoginPage();
productsPage=new ProductsPage();
System.out.println("Current test being executed "+m.getName());
}
@Test
public void invalidUserName() {
logInPage.enterUsername("Incorrect user");
logInPage.enterPassword("secret_sauce");
logInPage.clickLoginBtn();
String expectedMsg="Username and password do not match any user in this service.";
String errMsg=logInPage.getErrTxt();
System.out.println("Error message is "+ errMsg);
Assert.assertEquals(expectedMsg, errMsg);
}
@Test
public void invalidPassword() {
logInPage.enterUsername("standard_user");
logInPage.enterPassword("Incorrect Password");
logInPage.clickLoginBtn();
String expectedMsg="Username and password do not match any user in this service.";
String errMsg=logInPage.getErrTxt();
System.out.println("Error message is "+ errMsg);
Assert.assertEquals(expectedMsg, errMsg);
}
@Test
public void validCredentials() {
logInPage.enterUsername("problem_user");
logInPage.enterPassword("secret_sauce");
logInPage.clickLoginBtn();
String title="PRODUCTS";
String actTitle=productsPage.getTitle();
System.out.println("Actual title is "+ actTitle);
Assert.assertEquals(actTitle, title);
}
}
LoginPage.java
package com.qa.pages;
import org.openqa.selenium.support.PageFactory;
import com.qa.BaseTest;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
public class LoginPage {
BaseTest base;
public LoginPage() {
base=new BaseTest();
PageFactory.initElements(new AppiumFieldDecorator(base.getDriver()), this);
}
@AndroidFindBy(accessibility="test-Username")
private MobileElement userNameTxtFld;
@AndroidFindBy(accessibility="test-Password")
private MobileElement passWordTxtFld;
@AndroidFindBy(accessibility="test-LOGIN")
private MobileElement logInBtn;
@AndroidFindBy(xpath="//android.view.ViewGroup[@content-desc=\"test-Error message\"]/android.widget.TextView")
private MobileElement errMsg;
public LoginPage enterUsername(String userName) {
base.sendKeys(userNameTxtFld,userName);
return this;
}
public LoginPage enterPassword(String passWord) {
base.sendKeys(passWordTxtFld,passWord);
return this;
}
public String getErrTxt() {
return base.getAttribute(errMsg,"text");
}
public ProductsPage clickLoginBtn() {
base.click(logInBtn);
return new ProductsPage();
}
}
解释是@BeforeClass
注释。
您需要了解几件事。第一件事与关键字 static
有关。我将只涉及与此问题相关的信息。
然后 class 的字段标记为 static
,这意味着 class 的每个实例共享确切的变量。让我们看看你的 BaseTest
class:
public class BaseTest {
public BaseTest() {}
protected static AppiumDriver driver; // shared variable
...
protected String dir; // not shared
public void setDriver(AppiumDriver driver) {
this.driver=driver;
}
... // more stuff in the class
}
如果 class A 和 class B 都创建了 BaseTest
的实例,其中一个设置 AppiumDriver
,则第二个 class 不必如果它也创建自己的 BaseTest
实例,则设置一个驱动程序。同样,如果class B改变了AppiumDriver
的实例,它也改变了由class A创建的BaseTest
对象持有的AppiumDriver
对象。这就是将字段(变量)标记为静态的效果。
第二件事与执行顺序有关。请记住,如果 Class A 和 B 创建一个 BaseTest
的实例,一旦其中一个设置了 AppiumDriver
,它就会为两者设置它。但是,如果未设置驱动程序的 class 尝试首先使用它怎么办?在这种情况下,您将毫无疑问地得到 NullPointerException
。也就是说,正如@rzwitserloot 在他或她的 post 中提到的那样,执行顺序由注释 @BeforeClass
控制。所以你的教授是对的,因为,
- 实例化
BaseTest
的 class 有一个在执行任何测试之前调用的方法,该方法设置 AppiumDriver
对象,并且
AppiumDriver
对象是静态的,这意味着 BaseTest
的每个实例共享同一个驱动程序对象。
在 BaseTest.java 中,我有一个方法可以初始化 AppiumDriver 类型的对象。 在 LogInTest.java 中,我正在创建 BaseTest class 的实例并调用 initializeDriver 方法。 再次在 LoginPage.java 中,我正在创建另一个 BaseTest class 实例,但我没有调用 初始化驱动方法。因为在 LoginPage.java 中我只创建了一个 BaseTest class 的实例而不调用 initializeDriver 方法,所以与 LoginPage.java 的 BaseTest 实例相关联的驱动程序对象不应该为空吗?
这个程序按照我老师的解释工作,但我只是不明白它是如何工作的 LoginPage.java base.getDriver() 应该为空,因为我从未在那里调用过 initializeDriver 方法。
BaseTest.java
package com.qa;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.Properties;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import com.utils.TestUtils;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import io.appium.java_client.remote.MobileCapabilityType;
public class BaseTest {
public BaseTest() {
}
protected static AppiumDriver driver;
protected static Properties prop;
protected FileInputStream fis;
protected String dir;
public void setDriver(AppiumDriver driver) {
this.driver=driver;
}
public AppiumDriver getDriver() {
return driver;
}
public void initializeDriver(String platformName,String platformVersion, String deviceName) {
try {
dir=System.getProperty("user.dir");
fis=new FileInputStream(new File(dir+File.separator+"src"+File.separator+"main"+File.separator+"java"+File.separator+"com"+File.separator+"qa"+File.separator+"config.properties"));
prop=new Properties();
prop.load(fis);
DesiredCapabilities cap=new DesiredCapabilities();
cap.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName);
cap.setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion);
cap.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
cap.setCapability(MobileCapabilityType.AUTOMATION_NAME, prop.getProperty("UIAutomator2"));
//cap.setCapability(MobileCapabilityType.UDID, "50");
String appURL=dir+File.separator+prop.getProperty("appLocation");
cap.setCapability(MobileCapabilityType.APP, appURL); // if you want to install and launch a new app
cap.setCapability("autoGrantPermissions", true);
//cap.setCapability(MobileCapabilityType.BROWSER_NAME, "Chrome");
//cap.setCapability("chromedriverExecutable", "C:\chromedriver-mobile_browser\chromedriver.exe");
//cap.setCapability("newCommandTimeout", 900); //how long
cap.setCapability("appPackage", prop.getProperty("androidAppPackage"));
cap.setCapability("appActivity", prop.getProperty("androidAppActivity"));
cap.setCapability("autoAcceptAlerts", "true"); // to accept all alerts
//cap.setCapability("avdLaunchTimeout", "300000");
cap.setCapability("unlockType", "pin");
cap.setCapability("unlockKey", "74159");
URL url=new URL(prop.getProperty("appiumURL"));
driver=new AndroidDriver(url,cap);
String sessionID=driver.getSessionId().toString();
}
catch(Exception e) {
e.printStackTrace();
}
}
public void waitForVisibility(MobileElement e) {
WebDriverWait wait=new WebDriverWait(driver,TestUtils.WAIT);
wait.until(ExpectedConditions.visibilityOf(e));
}
public void click(MobileElement e) {
waitForVisibility(e);
e.click();
}
public void sendKeys(MobileElement e, String keys) {
waitForVisibility(e);
e.sendKeys(keys);
}
public String getAttribute(MobileElement e, String attribute) {
waitForVisibility(e);
return e.getAttribute(attribute);
}
public void quitDriver() {
driver.quit();
}
}
LogInTest.java
package com.qa.test;
import org.testng.annotations.Test;
import com.qa.BaseTest;
import com.qa.pages.LoginPage;
import com.qa.pages.ProductsPage;
import io.appium.java_client.AppiumDriver;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import java.lang.reflect.Method;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
public class LogInTest{
LoginPage logInPage;
ProductsPage productsPage;
BaseTest base;
AppiumDriver driver;
@Parameters({"platformName","platformVersion","deviceName"})
@BeforeClass
public void beforeClass(String platformName,String platformVersion,String deviceName) {
base=new BaseTest();
base.initializeDriver(platformName, platformVersion, deviceName);
}
@AfterClass
public void afterClass() {
base.quitDriver();
}
@BeforeMethod
public void beforeMethod(Method m) {
logInPage=new LoginPage();
productsPage=new ProductsPage();
System.out.println("Current test being executed "+m.getName());
}
@Test
public void invalidUserName() {
logInPage.enterUsername("Incorrect user");
logInPage.enterPassword("secret_sauce");
logInPage.clickLoginBtn();
String expectedMsg="Username and password do not match any user in this service.";
String errMsg=logInPage.getErrTxt();
System.out.println("Error message is "+ errMsg);
Assert.assertEquals(expectedMsg, errMsg);
}
@Test
public void invalidPassword() {
logInPage.enterUsername("standard_user");
logInPage.enterPassword("Incorrect Password");
logInPage.clickLoginBtn();
String expectedMsg="Username and password do not match any user in this service.";
String errMsg=logInPage.getErrTxt();
System.out.println("Error message is "+ errMsg);
Assert.assertEquals(expectedMsg, errMsg);
}
@Test
public void validCredentials() {
logInPage.enterUsername("problem_user");
logInPage.enterPassword("secret_sauce");
logInPage.clickLoginBtn();
String title="PRODUCTS";
String actTitle=productsPage.getTitle();
System.out.println("Actual title is "+ actTitle);
Assert.assertEquals(actTitle, title);
}
}
LoginPage.java
package com.qa.pages;
import org.openqa.selenium.support.PageFactory;
import com.qa.BaseTest;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
public class LoginPage {
BaseTest base;
public LoginPage() {
base=new BaseTest();
PageFactory.initElements(new AppiumFieldDecorator(base.getDriver()), this);
}
@AndroidFindBy(accessibility="test-Username")
private MobileElement userNameTxtFld;
@AndroidFindBy(accessibility="test-Password")
private MobileElement passWordTxtFld;
@AndroidFindBy(accessibility="test-LOGIN")
private MobileElement logInBtn;
@AndroidFindBy(xpath="//android.view.ViewGroup[@content-desc=\"test-Error message\"]/android.widget.TextView")
private MobileElement errMsg;
public LoginPage enterUsername(String userName) {
base.sendKeys(userNameTxtFld,userName);
return this;
}
public LoginPage enterPassword(String passWord) {
base.sendKeys(passWordTxtFld,passWord);
return this;
}
public String getErrTxt() {
return base.getAttribute(errMsg,"text");
}
public ProductsPage clickLoginBtn() {
base.click(logInBtn);
return new ProductsPage();
}
}
解释是@BeforeClass
注释。
您需要了解几件事。第一件事与关键字 static
有关。我将只涉及与此问题相关的信息。
然后 class 的字段标记为 static
,这意味着 class 的每个实例共享确切的变量。让我们看看你的 BaseTest
class:
public class BaseTest {
public BaseTest() {}
protected static AppiumDriver driver; // shared variable
...
protected String dir; // not shared
public void setDriver(AppiumDriver driver) {
this.driver=driver;
}
... // more stuff in the class
}
如果 class A 和 class B 都创建了 BaseTest
的实例,其中一个设置 AppiumDriver
,则第二个 class 不必如果它也创建自己的 BaseTest
实例,则设置一个驱动程序。同样,如果class B改变了AppiumDriver
的实例,它也改变了由class A创建的BaseTest
对象持有的AppiumDriver
对象。这就是将字段(变量)标记为静态的效果。
第二件事与执行顺序有关。请记住,如果 Class A 和 B 创建一个 BaseTest
的实例,一旦其中一个设置了 AppiumDriver
,它就会为两者设置它。但是,如果未设置驱动程序的 class 尝试首先使用它怎么办?在这种情况下,您将毫无疑问地得到 NullPointerException
。也就是说,正如@rzwitserloot 在他或她的 post 中提到的那样,执行顺序由注释 @BeforeClass
控制。所以你的教授是对的,因为,
- 实例化
BaseTest
的 class 有一个在执行任何测试之前调用的方法,该方法设置AppiumDriver
对象,并且 AppiumDriver
对象是静态的,这意味着BaseTest
的每个实例共享同一个驱动程序对象。