In my test class, I have DesiredCapabilities set up for Appium test. In that class, I initialised my BasePage class holding pagefactory elements. When I run the test, it works as expected.
Now, I tried to be a bit more creative by moving my DediredCapabilities into a separate class, CapacityManager. In my test class, I called the method holding the DesiredCapabilities from the CapacityManager. The method call was successful, my app was launched, but the pagefactory elements were no longer working. I am not sure why.
When I run the test, I get a nullPointerExceptio on the first mobile element and this makes me suspect that a pagefactory or driver initialisation problem.
The working test class looks like this:
public class LoginTest {
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
public static AndroidDriver<AndroidElement> driver;
public static File classpathRoot;
public void startApp() throws MalformedURLException {
classpathRoot = new File(System.getProperty("user.dir"));
File appDir = new File(classpathRoot, "");
File app = new File(appDir, baseUtil.getMyApp());
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
cap.setCapability(MobileCapabilityType.PLATFORM_VERSION, "11.0");
cap.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
cap.setCapability(MobileCapabilityType.AUTOMATION_NAME, "uiautomator2");
cap.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
URL url = new URL("http://localhost:4723/wd/hub");
driver = new AndroidDriver<>(url, cap);
basePage = PageFactory.initElements(driver, BasePage.class); //pagefactory initialised
}
public void login(String sheetname, int rowNumber) throws InterruptedException, IOException, InvalidFormatException {
FeatureUtils featureUtils = new FeatureUtils();
File excelDir = new File(classpathRoot, "");
File exceldoc = new File(excelDir, baseUtil.getUsersExcelDoc());
List<Map<String, String>> testData = featureUtils.getData(exceldoc.getAbsolutePath(), sheetname);
String username = testData.get(rowNumber).get("username");
String password = testData.get(rowNumber).get("password");
basePage.yesIAgreeButton.click(); //first element clicks successfully
This is the BasePage class holding the pagefactory elements:
public class BasePage {
private final WebDriver driver;
public BasePage(WebDriver driver) { //constructor
this.driver = driver;
}
@FindBy(id = "com.test")
public WebElement yesIAgreeButton;
With the two classes above, the LoginTest class works as expected.
Now, I removed the DesiredCapabilities from the test class and put them in a new class, CapacityManager:
public class CapacityManager {
public static AndroidDriver<AndroidElement> driver;
public static File classpathRoot;
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
public DesiredCapabilities appDesiredCapabilities() throws MalformedURLException {
DesiredCapabilities desiredCapabilities = null;
classpathRoot = new File(System.getProperty("user.dir"));
File appDir = new File(classpathRoot, "");
File app = new File(appDir, baseUtil.getMyApp());
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
cap.setCapability(MobileCapabilityType.PLATFORM_VERSION, "11.0");
cap.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
cap.setCapability(MobileCapabilityType.AUTOMATION_NAME, "uiautomator2");
cap.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
URL url = new URL("http://localhost:4723/wd/hub");
driver = new AndroidDriver<>(url, cap);
basePage = PageFactory.initElements(driver, BasePage.class); //pagefactory initialisation
return desiredCapabilities;
}
}
I then called the DesiredCapabilities from the test class, like this:
public class LoginTest {
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
public static AndroidDriver<AndroidElement> driver;
public static File classpathRoot;
CapacityManager capacityManager = new CapacityManager();
public void startApp() throws MalformedURLException {
capacityManager.appDesiredCapabilities(); //method call. App launches successfully.
}
public void login(String sheetname, int rowNumber) throws InterruptedException, IOException, InvalidFormatException {
FeatureUtils featureUtils = new FeatureUtils();
File excelDir = new File(classpathRoot, "");
File exceldoc = new File(excelDir, baseUtil.getUsersExcelDoc());
List<Map<String, String>> testData = featureUtils.getData(exceldoc.getAbsolutePath(), sheetname);
String username = testData.get(rowNumber).get("username");
String password = testData.get(rowNumber).get("password");
basePage.yesIAgreeButton.click(); //getting nullpointer error on this line. Looks like an issue with the pagefactory elements initialisation. basePage is null.
driver.pressKey(new KeyEvent(AndroidKey.TAB));
Objects represent real world entities. For example (taking a page out of the Head First Java reference), a cat is an object. A cat has legs and mouth as features which can be used for doing certain things like running and biting.
Likewise when you created the
CapacityManagerclass and callednewoperator on it in theLoginTest, you have created an object.CapacityManagerhasdriver, andBasePage(among other features) using which you have initialized in theappDesiredCapabilities()method.In the case of the Cat, we cannot use the cat's legs to run and use the cat's mouth to bite (there is obviously a way to do this in java but we are getting ahead of ourselves at this point). We can tell the cat to run and bite but in the end the cat has to do these operations by itself. Likewise,
CapacityManagerfeatures likedriverandBasePagecannot be used by us from outside theCapacityManager. We can call theCapacityManager.appDesiredCapabilities()method to initialize the driver, and set page factory onBasePagebut we cannot use them directly.Simply put, declaring
driverandBasePagein theLoginTestand creating an object ofCapacityManagerdoes not initialize thedriverandBasePagefeatures in theLoginTest. We have to figure out how to control thedriverandBasePagedeclared in theCapacityManagerinLoginTest. For this we usepublicaccess modifier in theCapacityManagerfordriverandBasePage. The correct OOPs way is to usegettersto make the features public so that we can get access to them from outside. This is shown in the below code update.There are a lot of different and better ways to configure this. If you are feeling even more fancy, you can create a class for getting the testdata. That is for another day.
Hopefully the solution above will solve the error you are getting!