Наиболее существенные из них:
- невозможность повторного использования кода page-объектов для страниц с одинаковыми элементами;
- плохая читаемость и отсутствие наглядности кода для страниц с большим количеством элементов;
- отсутствие типизации элементов.
Из этого поста вы узнаете, как мы в Яндексе решаем эти проблемы с помощью фреймворка с открытым исходным кодом Html Elements. Он расширяет концепцию шаблона Page Object и позволяет сделать взаимодействие с элементами на веб-страницах простым, гибким и удобным.
Мы не будем останавливаться на описании самого паттерна и его принципов, поскольку большинству из вас он наверняка хорошо знаком. Если же кто-то с ним не встречался, то узнать о нём можно из этого поста или мастер-класса. Также, говоря о применении паттерна Page Object, мы будем подразумевать его Java-реализацию в фреймворке Selenium WebDriver.
Повторное использование кода
Представьте, что вам понадобилось написать тесты не на какую-то отдельную страницу, а на весь веб-сервис. На его страницах наверняка будут встречаться общие блоки элементов: хедеры, футеры, возможно, какие-то одинаковые формы и т.д. Например, на главной странице Яндекса есть форма поиска, которая сохраняется и при переходе на страницу с поисковой выдачей.
Она встречается и на других сервисах Яндекса: например, на Яндекс.Авто, Яндекс.Маркете и Яндекс.Работе.
Форму авторизации тоже можно увидеть не только на главной странице, но и, к примеру, на странице Яндекс.Паспорта или на Яндекс.Маркете. Логика взаимодействия с общими блоками на каждой странице абсолютно одинакова. Но, когда вам понадобится писать page-объекты для этих страниц, вы будете вынуждены в каждом из них продублировать код, реализующий взаимодействие с этими блоками.
Вы, наверное, уже поняли, к чему я клоню? Да-да, было бы здорово иметь возможность описывать блоки элементов и логику взаимодействия с ними отдельно, и уже из них собирать page-объекты. И фреймворк Html Elements позволяет это делать. Например, опишем с его помощью поисковую форму:
@Block(@FindBy(className = "b-head-search")) public class SearchArrow extends HtmlElement { @FindBy(name = "text") private WebElement requestInput; @FindBy(xpath = "//input[@type='submit']") private WebElement searchButton; public void search(String request) { requestInput.sendKeys(request); searchButton.click(); } }
А также форму авторизации:
@Block(@FindBy(className = "b-domik__form")) public class AuthorizationForm extends HtmlElement { @FindBy(id = "b-domik-username") WebElement loginField; @FindBy(id = "b-domik-password") WebElement passwordField; @FindBy(xpath = "//input[@type='submit']") WebElement submitButton; public void login(String login, String password) { loginField.sendKeys(login); passwordField.sendKeys(password); submitButton.click(); } }
Тогда page-объект для главной страницы Яндекса будет выглядеть так:
public class SearchPage { private SearchArrow searchArrow; private AuthorizationForm authorizationForm; // Other blocks and elements here public SearchPage(WebDriver driver) { HtmlElementLoader.populatePageObject(this, driver); } public void search(String request) { searchArrow.search(request); } public void login(String login, String password) { authorizationForm.login(login, password); } // Other methods here }
Кстати, вы заметили, что селекторы элементов блока задаются относительно селектора самого блока? Это очень удобно, поскольку блок может находиться на разных страницах по разным селекторам. При этом внутренняя структура блока изменяться не будет. В таком случае при включении блока в page-объект достаточно перегрузить селектор самого блока. К примеру, на страницах сервиса Яндекс.Авто поисковую форму следует искать иначе, чем на главной странице:
public class AutoHomePage { @FindBy(className = "b-search") private SearchArrow searchArrow; // Other blocks and elements here public AutoHomePage(WebDriver driver) { HtmlElementLoader.populatePageObject(this, driver); } public void search(String request) { searchArrow.search(request); } // Other methods here }
Читаемость и наглядность
Чтобы полностью покрыть ту или иную страницу веб-сервиса тестами, вам понадобится использовать все ее элементы. А их может быть очень много. К примеру, на главной странице Яндекс.Авто есть форма поиска автомобиля по параметрам. На ней и так более 30 элементов с учетом расширенного поиска, а ещё список марок автомобилей, блок новостей, блок автомобильных новинок и т.д.
Если мы напишем page-объект для этой страницы, используя только возможности фреймворка Selenium WebDriver, то получим очень большой класс с длинным полотном элементов и огромным количеством методов, реализующих взаимодействие со всеми этими элементами. Согласитесь, такой класс будет очень ненаглядным и плохо читаемым.
Но если у вас есть возможность отдельно создавать блоки элементов, то эта проблема тоже решается. Page-объект будет содержать всего несколько блоков, а их структура и логика взаимодействия с ними будет описана отдельно.
Типизация элементов
В Selenium WebDriver все элементы страницы — будь то кнопка, чекбокс или поле текстового ввода — описываются при помощи интерфейса WebElement. Поэтому он имеет много методов, которые свойственны элементам разного типа. Но если мы, например, взаимодействуем с кнопкой, то нам вряд ли захочется вбивать туда текст.
С другой стороны, на страницах часто присутствуют сложные элементы, взаимодействие с которыми нельзя описать при помощи одного только WebElement’а. Скажем, группа radio-button’ов, выпадающий список или поле выбора даты.
В обоих случаях напрашивается одно и то же решение: ввести типизированные элементы, которые будут в первом случае сужать интерфейс WebElement’а, а во втором — реализовывать взаимодействие с более сложными элементами. Это мы и сделали в фреймворке Html Elements.
Например, так будет выглядеть описание поисковой формы с использованием типизированных элементов:
@Block(@FindBy(className = "b-head-search")) public class SearchArrow extends HtmlElement { @FindBy(name = "text") private TextInput requestInput; @FindBy(xpath = "//input[@type='submit']") private Button searchButton; public void search(String request) { requestInput.sendKeys(request); searchButton.click(); } }
А так будет выглядеть описание формы для выбора языка в настройках поиска, где есть выпадающий список:
@Block(@FindBy(id = "lang")) public class LanguageSelectionForm extends HtmlElement { @FindBy(className = "b-form__select") private Select listOfLanguages; @FindBy(xpath = "//input[@type='submit']") private Button saveButton; @FindBy(xpath = "//input[@type='button']") private Button returnButton; public void selectLanguage(String language) { listOfLanguages.selectByValue(language); saveButton.click(); } }
Мы уже реализовали поддержку таких базовых элементов, как TextInput, Button, CheckBox, Select, Radio и Link. Вы тоже можете очень просто писать свои собственные типизированные элементы и расширять уже существующие.
***
Фреймворк Html Elements — это инструмент, который позволяет собирать page-объекты как конструктор. Из типизированных элементов вы можете собирать нужные вам блоки, которые можно объединять, комбинировать друг с другом и собирать из них page-объекты. Это значительно повышает степень переиспользования кода, делает его более читаемым и наглядным, а написание тестов — более простым. Html Elements выложен в open source. Попробовать его в деле и посмотреть код можно тут.
В одном из следующих постов о тестировании в Яндексе мы подробнее расскажем о самом фреймворке. Вы узнаете, какие еще полезные возможности у него есть и как с его помощью удобно тестировать веб-интерфейсы.
ссылка на оригинал статьи http://habrahabr.ru/company/yandex/blog/158787/
Добавить комментарий