Блоки MobileElement & Page Factory. Как это сделать?

Всем привет.

Кто нибудь строил фреймворки с использованием Page Factory, и созданием собственных блоков элементов (для их многократного переиспользования в различных пейджах), по аналогии с тем, как это можно делать в HTML Elements?

Привет, да.
Что именно интересует?

интересует почему вылетает IllegalArgumentException на инициализации кастомного блока, который я заэкстендил от MobileElement.
У меня есть:

public abstract class BaseScreen {
    protected AppiumDriver<MobileElement> driver;

    public BaseScreen(AppiumDriver<MobileElement> driver) {
        this.driver = driver;
        PageFactory.initElements(new AppiumFieldDecorator(driver), this);
    }
}

Собственно сам пейдж

public class SomeOtherScreen extends BaseScreen {
    @AndroidFindBy(xpath = "some locator")
    private MyElementsBlock myElementsBlock; //реализация блока ниже

    @AndroidFindBy(xpath = "some locator")
    private MobileElement loginButton;

    public SomeOtherScreen(AppiumDriver<MobileElement> driver) {
        super(driver);
    }

    //some other methods to perform actions on screen
}
public class MyElementsBlock extends MobileElement {
    @AndroidFindBy(xpath = "some locator")
    private List<SomeOtherBlock> someOtherBlocksList;

    @AndroidFindBy(xpath = "some locator")
    private List<MobileElement> mobileElementsList;

}

падает на “PageFactory.initElements(new AppiumFieldDecorator(driver), this)” в конструкторе BaseScreen, когда начинает инициализировать элементы пейджа “SomeOtherScreen”. java.lang.IllegalArgumentException: Can not set com.application.blocks.MyElementsBlock field com.application.screens.SomeOtherScreen.myElementsBlock to io.appium.java_client.android.AndroidElement$$EnhancerByCGLIB$$b598166c

Ну ошибка говорит о том, что ваш блок не является типом AndroidElement.
Для инициализации блоков нужен другой декоратор, полагаю. Хз как там в джаве с этим обстоят дела конечно, но думаю что копать нужно в этом направлении.
Т.е. классический декоратор, который знает как инициализировать поля-элементы, не знает как инициализировать поля-блоки.

1 лайк

А у тебя в чем делалось такое?

У меня на шарпе написан декоратор для блоков и своих элементов (т.е. возможность инициализации элементов собственных классов, о чём вы писали про HTML Elements)

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

Страница имеет такой код в конструкторе:

PageFactory.InitElements(DriverManager.Driver, this, new AFieldDecorator());

Декоратор передаётся без аргументов.

Когда внутри этого метода мы, проходя по всем полям в классе страницы, натыкаемся на поле с типом блок, мы создаём инстанс блока, используя его конструктор (кол-во аргументов у вас может отличаться):

protected ABlock(IElementLocator locator, By bys, string elementTitle)
        {
            Bys = bys;
            _locator = locator;
            Title = elementTitle;
            PageFactory.InitElements(DriverManager.Driver, this, new AFieldDecorator(this));
        }

Который в свою очередь вызывает свой InitElements для элементов внутри себя, передавая в декоратор аргумент - инстанс самого блока (в моём случае это требуется для доп. логики, которая выполняется для элементов внутри блока, но в классическом варианте это необязательно)

Блок сам знает как ему инициализировать свои поля, и он должен это делать сам.

1 лайк

Спасибо за подсказку.
Попробую реализовать свой декоратор.