Как менялось мое отношение к PageObject
Во многих сообществах тестировщиков холиварят о том, как должен выглядеть PageObject. Одни кричат, что нужно хранить локаторы в полях, другие выступают только за методы. Должен PageObject содержать логику или нет? Как и в любом холиваре, каждая из сторон считает свою точку зрения самой правильной. Как участник социума и человек, сидящий в Slack сообществе тестировщиков, я тоже наблюдаю эти перепалки. У меня есть свое мнение по этому поводу, но перед тем, как его выразить, сначала опишу историю преобразования PageObject на различных проектах, в которых я принимал участие.
Для меня все началось еще в далеком 2012 году, когда я попал на проект, на котором только зарождалась автоматизация. Так как я был совсем неопытным Junior’ом, я впитывал и делал так, как мне говорили.
PageObject на том проекте выглядел примерно так:
public class Portfolios extends BasePage {
private Actions builder = new Actions(getDriverProvider().get());
public Portfolios(WebDriverProvider driverProvider) {
super(driverProvider);
}
public void selectActionMenuItem(String name) {
clickActionsButton();
waitForVisible(locators.ACTIONS_MENU_ITEM_BY(name)).click();
}
private void openNodePortfolioFolder() {
find(locators.NAVIGATION_TREE_PORTFOLIOS_FOLDER()).click();
waitForVisible(By.xpath("//a[contains(@title,'BOOK:')]"));
}
public boolean isActionsMenuItemDisplayed(String menuItem) {
return isDisplayed(locators.ACTIONS_MENU_ITEM_BY(menuItem));
}
public void actionsCreateVirtualPortfolio() {
WebElement createPortfolio = waitForVisible(locators
.ACTIONS_CREATE_VIRTUAL_PORTFOLIO());
createPortfolio.click();
}
public void actionsCreatePortfolioLinks() {
waitForVisible(BaseAraPage.locators.getACTIONS_CREATE_PORTFOLIO_LINKS())
.click();
}
Да, все выглядит ужасно! Проект писался на базе самописной обертки для Selenium Webdriver. Локаторы выносились с отдельный класс Locators.
В целом нам удавалось более-менее успешно с этим жить, но, смотря на это все, я, конечно, понимаю, что такая реализация является категорически неприемлимой в наше время.
Page Object на базе @FindBy и Component object
В следующем проекте мы пробовали описывать страницы примерно так:
public class DictionaryPage extends PageObject {
@FindBy(name="search")
private WebElementFacade searchTerms;
@FindBy(name="go")
private WebElementFacade lookupButton;
public void enter_keywords(String keyword) {
searchTerms.type(keyword);
}
public void lookup_terms() {
lookupButton.click();
}
Лично мне такой подход не нравится тем, что нужно инициализировать страницы через PageFactory
PageFactory.initElements(DictionaryPage.class)
К тому же, при большом количестве елементов на странице такой PageObject класс обростает уж очень большим количеством полей, помарканных аннотацией @FindBy. Локаторы неудобно переиспользовать.
HTML Elements
Естественно, был этап, когда появилась библиотека Yandex HTML elements и я пробовал ее использовать, но мне не понравилось. Просто не понравилось и все. Такое бывает.
PageObject с полями By
В попытке улучшить предыдущую реализацию мы пробовали писать PageObject в таком стиле:
public class DictionaryPage extends PageObject {
private By searchTerms = By.name("search");
private By lookupButton = By.name("go");
public void enter_keywords(String keyword) {
$(searchTerms).type(keyword);
}
public void lookup_terms() {
$(lookupButton).click();
}
В принципе, мне все нравилось, пока количество елементов было небольшим. По мере роста объема елементов такие страницы было неудобно поддерживать. Постоянно нужно было скролить вверх, чтобы подправить локатор, потом возвращаться к месту использования этого локатора.
Эра Selenide
Пока я там ковырялся со своими велосипедами и экспериментировал с реализациями PageObject, библиотека Selenide росла и крепла, и я начал использовать ее на боевых проектах.
Реализация PageObject стала выглядеть так:
public class MainPage {
public void enter_keywords(String keyword) {
$(name("search")).type(keyword);
}
public void lookup_terms() {
$(".lookupButton").click();
}
Локаторы хранятся прямо там, где они используются - это удобно, ты заходишь в метод и сразу видишь, что там происходит. Такой подход позволяет экономить время и нервы тем, кто пишет и поддерживает тесты.
Был еще один проект, на котором я использовал Groovy, и там у нас была возможность писать PageObject в очень интересном формате.
Такой подход мне нравится больше всего, но, увы, он доступен только при использовании Groovy.
P/S
К более-менее удобному для себя формату реализации PageObject я пришел через опыт, а он может быть положительным и отрицательным. Я экспериментировал и нашел самый удобный для себя формат. Считаю, что наличие холиваров - это отлично, главное - относиться к ним здраво и выносить полезный опыт.