Appium PageObject — когда/где создавать экземпляр страницы?

В моей команде мы проводим кроссплатформенное тестирование пользовательского интерфейса с использованием Appium и Appium Java-Client. Текущая структура нашего проекта выглядит примерно так:

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. В настоящее время у нас есть метод @Before в нашем SignInSteps, который выполняется перед запуском каждого сценария Gherkin (что не идеально), но это помогает нам иметь свойство SignInPage в классе SignInSteps, которое повторно используется всеми шагами.

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
}

Однако я считаю, что более чистым подходом было бы создание SignInPage в качестве локальной переменной внутри каждого пошагового метода в SignInSteps. Влияет ли какое-либо влияние на производительность при создании страниц, которые вам нужны на каждом этапе?

Кроме того, мне непонятно, с нашим текущим подходом (подход @Before), почему именно он работает, даже когда вы создаете страницу для некоторых шагов, которые будут выполняться позже (поэтому экран в этот момент даже не виден) .

Так что, может быть, более важный вопрос будет заключаться в том, как искать элементы? Это при вызове PageFactory.initElements (новый AppiumFieldDecorator (драйвер, 15, TimeUnit.SECONDS), это); или при фактическом доступе к аннотированным свойствам (что было бы каким-то ленивым подходом к инициализации, которого, насколько мне известно, у Java нет, если только мое понимание аннотаций Java неверно).

Извините за длинный пост, но это некоторые вещи, которые я хочу понять полностью. Так что любая помощь высоко ценится.

Благодарю вас!


person Cosmin    schedule 28.11.2016    source источник


Ответы (1)


Я провел еще несколько исследований (отладки) и нашел ответ:

Когда вы вызываете PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);, аннотированные свойства со страницы устанавливаются (декорируются) посредством отражения (см. AppiumFieldDecorator) с прокси-сервером (ElementInterceptor), который обертывает MobileElement. Каждый раз, когда вы вызываете метод для аннотированного свойства, вы фактически вызываете прокси-сервер, который ищет элемент и перенаправляет вызов метода. Между ними нет кеша (в отличие от WidgetInterceptor, который я еще не понял, где он используется).

Таким образом, в моем случае создание страницы один раз или на каждом этапе не имеет большого значения, потому что поиск элемента выполняется каждый раз, когда вы с ним взаимодействуете (что, я думаю, хорошо, но это также может повлиять на производительность).

Я также прикрепил несколько скриншотов ниже:

Stacktrace при вызове PageFactory.initElements (новый AppiumFieldDecorator (драйвер, 15, TimeUnit.SECONDS), this);

введите здесь описание изображения

введите здесь описание изображения

Stacktrace при вызове click для элемента

введите здесь описание изображения

введите здесь описание изображения

Надеюсь, это поможет другим понять, как работает инструмент.

person Cosmin    schedule 04.12.2016