【问题标题】:Appium PageObject - When / Where to instantiate the page?Appium PageObject - 何时/何处实例化页面?
【发布时间】:2016-12-04 19:37:16
【问题描述】:

在我的团队中,我们正在使用 Appium 和 Appium Java-Client 进行跨平台 UI 测试。 我们项目的当前结构是这样的:

mobile
   pages
     SignInPage
   steps
     SignInSteps

使用 Cucuember 将这些步骤“粘合”在一起。 SignInPage 看起来像这样:

public class SignInPage {

    public SignInPage(AppiumDriver driver) {
        PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);
    }

    // region Identifiers
    final String IOS_USERNAME_FIELD = "SignInUsernameField";
    final String ANDROID_USERNAME_FIELD = "new UiSelector().resourceIdMatches(\".*id/username.*\")";
    final String IOS_PASSWORD_FIELD = "SignInPasswordField";
    final String ANDROID_PASSWORD_FIELD = "new UiSelector().resourceIdMatches(\".*id/password_editText.*\")";
    final String IOS_SIGN_IN_BUTTON = "SignInButton";
    final String ANDROID_SIGN_IN_BUTTON = "new UiSelector().resourceIdMatches(\".*id/signInButton.*\")";
    // endregion

    @iOSFindBy(accessibility = IOS_USERNAME_FIELD)
    @AndroidFindBy(uiAutomator = ANDROID_USERNAME_FIELD)
    private MobileElement usernameField;

    @iOSFindBy(accessibility = IOS_PASSWORD_FIELD)
    @AndroidFindBy(uiAutomator = ANDROID_PASSWORD_FIELD)
    private MobileElement passwordField;

    @iOSFindBy(accessibility = IOS_SIGN_IN_BUTTON)
    @AndroidFindBy(uiAutomator = ANDROID_SIGN_IN_BUTTON)
    private MobileElement signInButton;

    public MobileElement getUsernameField() {
        return usernameField;
    }

    public MobileElement getPasswordField() {
        return passwordField;
    }

    public MobileElement getSignInButton() {
        return signInButton;
    }

    public void tapUsernameField() {
        getUsernameField().click();
    }

    public void tapSignInButton() {
        getSignInButton().click();
    }

    public void clearUsernameEditText() {
        getUsernameField().clear();
    }
}

在性能和元素查找方面,我们不确定在哪里创建 SignInPage 的实例最好。目前,我们在 SignInSteps 中有一个 @Before 方法,该方法在每个 Gherkin 场景开始之前执行(这并不理想),但它有助于我们在 SignInSteps 类中拥有一个可供所有步骤重用的 SignInPage 属性。

public class SignInSteps {

    private SignInPage signInPage;
    AppiumDriver driver;

    @Before()
    public void setUp() throws MalformedURLException {
        driver = TestBase.getInstance().getDriver();
        signInPage = new SignInPage(driver);
    }

    @Given("I fill in the username and password")
    public void fill_username_and_password() throws Throwable {
        signInPage.tapUsernameField();
        signInPage.clearUsernameEditText();
        fillEditText(signInPage.getUsernameField(), PropertiesManager.getInstance().getValueForKey(Constants.SIGN_IN_USERNAME));
        fillEditText(signInPage.getPasswordField(), PropertiesManager.getInstance().getValueForKey(Constants.SIGN_IN_PASSWORD));
    }
  // Other sign in steps below
}

但是我觉得更简洁的方法是在 SignInSteps 的每个步骤方法中创建 SignInPage 作为局部变量。在每个步骤中创建您需要的页面是否会对性能产生影响?

另外,我不清楚,使用我们当前的方法(@Before 方法)为什么即使您为稍后将执行的某些步骤创建页面(因此屏幕在这一点)。

所以也许更大的问题是如何查找元素?是不是在调用 PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);或者在实际访问带注释的属性时(这将是某种惰性初始化方法,据我所知 Java 没有,除非我对 Java 注释的理解是错误的)。

对不起,这篇文章很长,但这些是我想彻底理解的一些事情。因此,我们非常感谢任何帮助。

谢谢!

【问题讨论】:

    标签: appium cucumber-jvm pageobjects java-client


    【解决方案1】:

    我做了更多的研究(调试),我找到了答案:

    当您调用PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this); 时,页面中的注释属性通过反射(请参阅AppiumFieldDecorator)设置(装饰),并使用包装MobileElement 的代理(ElementInterceptor)。每次调用带注释的属性的方法时,实际上都会调用查找元素并转发方法调用的代理。两者之间没有缓存(与 WidgetInterceptor 不同,我还没有弄清楚它的使用位置)。

    因此,在我的情况下,创建页面一次或在每个步骤中并没有真正的区别,因为每次与它交互时都会执行元素查找(我想这很好,但它可能会对性能产生影响也)。

    我还附上了下面的一些截图:

    调用 PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this) 时的 Stacktrace;

    在元素上调用 click 时的堆栈跟踪

    希望这有助于其他人也了解该工具的工作原理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-31
      • 2017-10-12
      • 2015-08-23
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      • 2019-01-13
      • 2022-01-03
      相关资源
      最近更新 更多