从不同的 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 控制。所以你的教授是对的,因为,

  1. 实例化 BaseTest 的 class 有一个在执行任何测试之前调用的方法,该方法设置 AppiumDriver 对象,并且
  2. AppiumDriver 对象是静态的,这意味着 BaseTest 的每个实例共享同一个驱动程序对象。