Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Нет ли никакого подвоха в создании static локаторов?


(You Rooock) #1

Привет всем!
Автоматизирую на C# + Selenium и вот появился такой вопрос :
а нет ли ничего такого в использовании статических локаторов? Просто раньше объявлял типа private, а теперь появилась необходимость в private static.
Так вот, не вылезет ли это где-то боком?

З.Ы. Ниже приведен код, в котором необходимо использовать статический локатор.

private Dictionary<string, Func<string>> _fieldMessagesDictionary =
    new Dictionary<string, Func<string>>
    {
    { "Current Password", () => getTextAttribute(currentPasswordLocator) },
    { "New Password", () => getTextAttribute(newPasswordLocator) },
    { "Confirm Password", () => getTextAttribute(confirmPasswordLocator) }
    };

(Sergey Korol) #2

Если у вас появилась необходимость в статических локаторах, значит вы архитектурно что-то делаете не так. Зачем вам в мапе хранить harcoded expected vs actual значения? Как же DDT / универсальность и т.п.? При изменении лейблы будете каждый раз лезть в код и править словарик? Почему не читать данные по-нормальному, к примеру, через БД? Или в приведенном коде я потерял какой-то скрытый сакральный смысл? Тогда будьте добры вначале объяснить идею, зачем вам это надо.


(You Rooock) #3

Раньше был метод, в котором по switch case шло разделение по названию полю ( “current password” e.g.). То есть, передаем название поля в метод, а он возвращает значение этого поля. Но эта конструкция занимает довольно много места и захотелось оптимизировать код, использовав словарик. Но вот тут и возник вопрос по поводу целесообразности этого всего.
Из вашего комментария я понимаю, что стоит избегать этого, верно?


(Sergey Korol) #4

Ну из теории программирования вы наверняка знаете, зачем нужны статические модификаторы, и чем чревато их использование в многопоточном окружении. К тому же, комбинация private static больше подходит для использования в качестве внутренних классовых констант. Но локаторы по своей сути не должны быть константами, т.к. в зависимости от выбранной техники, вы не всегда сможете (да и нужно ли вообще?) их сразу же инициализировать.

Исходя из вашего разъяснения, я так понимаю, идея заключалась в маппинге филдов с паблик константами для быстрого доступа из-вне PageObject’а? Что-то вроде generic verification для приватных элементов?

Если так, то проще всего будет создать один единственный класс таких констант. Не уверен, как это в C# делается, но в Java можно создать вложенную структуру из статически классов по типу:

public class PageElement {
    public static class Login {
            public static class Button {
                    public static final String LOG_ON = "BUTTON_LOG_ON";
            }
    }
}

Подобная конструкция даст вам возможность из любой точки приложения обращаться к константам путем цепочечного вызова:

PageElement.Login.Button.LOG_ON

Такой элемент можно в итоге замапить с вашим локатором. Но именно с локатором, а не со значением какой-то лейблы.

private Map<String, HTMLElement> elements = new HashMap<>(); // аналог словарика

При организации правильной структуры наследования, этот словарик можно поместить в вашем классе-родителе BasePage. Но тут встает вопрос в том, как все это дело теперь замапить?

В Java есть гибкая система аннотаций. По-моему, в C# тоже есть нечто подобное. Так вот можно создать кастомную аннотацию со спец. параметром - ссылкой на нашу константу.

@HTML(searchBy = ID, value = "locator", ref = Button.LOG_ON)
private HTMLElement buttonLogOn;

В итоге, когда при инициализации пейджи мы будем спускаться по цепочке наследования к BasePage, там же мы можем чекать кастомную аннотацию и динамически инициализировать наши филды. Приблизительно так же, как организована связка @FindBy + WebElement + initElements.

В итоге, вы получите возможность создавать generic методы, которые по ключу-константе смогут отдавать вам локатор, по типу:

// element = ref, e.g. Button.LOG_ON
public void verifyTextEquals(BasePage page, String element, String expected) {
    getSoftAssert().assertEquals(page.getText(element), expected);
}
// call
verifyTextEquals(loginPage, Button.LOG_ON, "Log On");

К сожалению, такая схема потребует определенных знаний и усилий в реализации. Но зато вы лишите себя “счастья” создавать миллионы однотипных геттеров и свитчей внутри пейджей.


(You Rooock) #5

Cпасибо большое! Постараюсь что-то подобное реализовать!


(Sergey Korol) #6

Не забываем ставить лайки. :wink: