Appium PageObject - 何时/何地实例化页面?

时间:2016-11-28 17:55:37

标签: appium cucumber-jvm pageobjects java-client

在我的团队中,我们使用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(新的AppiumFieldDecorator(driver,15,TimeUnit.SECONDS)时);或者在实际访问带注释的属性时(除非我对Java注释的理解是错误的,否则这将是我认为Java没有的某种延迟初始化方法。)

对于长篇文章感到抱歉,但这些是我想彻底了解的一些内容。所以任何帮助都非常感谢。

谢谢!

1 个答案:

答案 0 :(得分:1)

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

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

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

我还附上了一些截图:

当您调用PageFactory.initElements(新的AppiumFieldDecorator(驱动程序,15,TimeUnit.SECONDS)时)的Stacktrace,这个);

enter image description here

enter image description here

在元素

上调用click时的Stacktrace

enter image description here

enter image description here

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