Search code examples
javaseleniumappium

Instantiating the same class from different classes


In BaseTest.java I have a method to initialize an object of the type AppiumDriver. In LogInTest.java I'm creating an instance of BaseTest class and calling the initializeDriver method. And again in LoginPage.java I'm creating another instance of the BaseTest class but I'm not calling the initializeDriver method. Since in LoginPage.java I'm only creating an instance of BaseTest class and not calling initializeDriver method, isn't the driver object associated BaseTest instance of LoginPage.java supposed to be null?

The program works as my teacher explained, but I just don't understand how it works as in LoginPage.java base.getDriver() should be null as I never called the initializeDriver method there.

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();


}
}

Solution

  • There are a couple of things you need to understand. The first thing has to do with the keyword static. I am only going to touch on information related to this issue.

    Then a class has fields marked at static, that means that every instance of that class share the exact variable. Let look at your 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
    }
    

    If class A and class B both create an instance of BaseTest and one sets AppiumDriver, the second class doesn't have to set a driver if it also creates its own instance of BaseTest. Likewise, if class B changes the instance of AppiumDriver, it also changes the AppiumDriver object held by the BaseTest object created by class A. This is the effect of marking a field (variable) as static.

    The second thing has to do with order of execution. Remember, if both Class A and B create an instance of BaseTest once one sets AppiumDriver it sets it for both. HOWEVER, what if the class that doesn't set the driver attempts to use it first? In that case, you will get a NullPointerException without a doubt. That said, as @rzwitserloot mentioned in his or her post, order of execution is controlled by the annotation @BeforeClass. So your professor is right because,

    1. The class that instantiates BaseTest has a method that gets invoked before any test gets executed and this method sets the AppiumDriver object, and
    2. The AppiumDriver object is static, which means every instance of BaseTest share the same driver object.