为什么从另一个类初始化时 pagefactory 类返回 null [英] Why is a pagefactory class returning null when initialised from another class

查看:43
本文介绍了为什么从另一个类初始化时 pagefactory 类返回 null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的测试类中,我为 Appium 测试设置了 DesiredCapabilities.在那个类中,我初始化了包含 pagefactory 元素的 BasePage 类.当我运行测试时,它按预期工作.

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.

现在,我试图通过将我的 DediredCapabilities 移动到一个单独的类 CapacityManager 来变得更有创意.在我的测试类中,我从容量管理器中调用了保存 DesiredCapabilities 的方法.方法调用成功,我的应用程序已启动,但 pagefactory 元素不再工作.我不知道为什么.

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.

当我运行测试时,我在第一个移动元素上得到一个 nullPointerExceptio,这让我怀疑是 pagefactory 或驱动程序初始化问题.

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.

工作测试类如下所示:

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

这是包含 pagefactory 元素的 BasePage 类:

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;

对于上述两个类,LoginTest 类按预期工作.

With the two classes above, the LoginTest class works as expected.

现在,我从测试类中删除了 DesiredCapabilities 并将它们放在一个新类中,CapacityManager:

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;
  }
   
}
    

然后我从测试类中调用了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)); 

推荐答案

CapacityManager里面的basePageLoginTest里面的两个不同的对象实例和 LoginTest 中的一个从未被初始化.您需要做的就是这样:

The basePage inside of CapacityManager and the one inside LoginTest are two different objects instances and the one inside LoginTest was never initialized. All you need to do is something like this:

basePage = capacityManager.getBasePage(); // create this method returning basePage object
basePage.yesIAgreeButton.click();

或者...因为basePagestatic,将它公开并静态调用它CapacityManager.basePage.也就是说,我认为这些 static 字段中的任何一个都不应该是 static.

Or... since basePage is static, make it public and call it statically CapacityManager.basePage. That said, I don't think any of those static fields should be static.

CapacityManager 类的构造很差.对于初学者来说,它有私有的静态字段,在初始化后从不用于任何事情.我不会修复所有内容,但我会修复与这篇文章相关的内容.将 CapacityManager 替换为我的版本并更新以解决其他字段的问题.

The class CapacityManager is poorly constructed. For starters, it has private static fields that are never used for anything after being initialized. I won't fix everything, but I would fix what pertains to this post. Replace CapacityManager with my version and update to fix the issue with the other fields.

public class CapacityManager {

  public AndroidDriver<AndroidElement> driver;
  public File classpathRoot;
  private final BaseUtil baseUtil = new BaseUtil();
  private BasePage basePage;
  private DesiredCapabilities cap;
  private File app;

  public CapacityManager() {
    classpathRoot = new File(System.getProperty("user.dir"));
    File appDir = new File(classpathRoot, "");
    app = new File(appDir, baseUtil.getMyApp());
  }

  public DesiredCapabilities appDesiredCapabilities() throws MalformedURLException {
    if (cap == null) {
        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);
    }

    return cap;
  }

  public BasePage getBasePage() {
      return basePage;
  }
}

无需更改 LoginTest 类中的任何其他内容(我假设它正确"工作),只需通过调用 getter 方法获取 BasePage:

Without changing anything else in the LoginTest class (I assume it works "correctly"), simply get the BasePage by calling the getter method:

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 basePage = PageFactory.initElements(capacityDriver.driver, BasePage.class); // pass the AndroidDriver instance in CapacityDriver class
    basePage.clickYesIAgree();
}

更新 #2

通常,当您创建工厂以提供类的实例时,您会隐藏该类的构造函数.当您拥有类的公共构造函数时,有时最好不要拥有工厂.为什么?以免混淆.当然,我刚才提到的也有很多例外.在这里,我们有一个令人困惑的实现.

UPDATE #2

Typically, when you create a factory to provide instances of a class, you hide the constructor of that class. When you have public constructors of a class, sometimes is better not to have a factory. Why? To avoid confusion. Of course, there are plenty of exceptions to what I just mentioned. Here, we have one of those confusing implementations.

您有一个带有公共构造函数的 BasePage,该构造函数将 WebDriver 作为参数.您还有 PageFactory.initElements(driver, BasePage.class) 返回 BasePage 的实例.我应该如何创建 BasePage 的实例?什么时候应该使用工厂?我什么时候应该直接调用构造函数?

You have a BasePage with a public constructor that takes a WebDriver as an argument. You also have PageFactory.initElements(driver, BasePage.class) that returns an instance of BasePage. How should I create an instance of BasePage? When should the factory be used? When should I call the contructor directly?

考虑到 PageFactory 是 Selenium 库提供的一个经过验证的类,有理由相信 BasePage 的结构不正确和/或 的使用@FindBy 注释不正确.我无法为您解决这些问题,但我可以建议我认为 BasePage 应该是什么样子.给你:

Considering that PageFactory is a proven class provided by the Selenium library, it is reasonable to believe that BasePage is ill contructed and/or the use of the @FindBy annotation is incorrect. None of those I can fix for you, but I can suggest what I believe BasePage should look like. Here you go:

public class BasePage {
    @FindBy(id = "com.test") // this component ID looks suspicious
    private WebElement yesIAgreeButton;

    public void clickYesIAgree() {
        yesIAgreeButton.click();
    }
}

第二次更新后,之前的代码已经更新.

The previous code has been updated after this second update.

这篇关于为什么从另一个类初始化时 pagefactory 类返回 null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆