company_banner

Тестирование в Яндексе. Фреймворк HTML Elements: чего не хватает в Page Object, и как это исправить

    Если вы занимаетесь тестированием веб-интерфесов, то наверняка задумывались о том, как сделать взаимодействие с веб-страницами в тестах максимально удобным. Среди тестировщиков очень широко известен шаблон проектирования Page Object. Но, несмотря на множество плюсов, у этого подхода есть и некоторые недостатки, которые сильно затрудняют его применение.

    Наиболее существенные из них:
    • невозможность повторного использования кода page-объектов для страниц с одинаковыми элементами;
    • плохая читаемость и отсутствие наглядности кода для страниц с большим количеством элементов;
    • отсутствие типизации элементов.

    Из этого поста вы узнаете, как мы в Яндексе решаем эти проблемы с помощью фреймворка с открытым исходным кодом HTML Elements. Он расширяет концепцию шаблона Page Object и позволяет сделать взаимодействие с элементами на веб-страницах простым, гибким и удобным.

    Мы не будем останавливаться на описании самого паттерна и его принципов, поскольку большинству из вас он наверняка хорошо знаком. Если же кто-то с ним не встречался, то узнать о нём можно из этого поста или мастер-класса. Также, говоря о применении паттерна Page Object, мы будем подразумевать его Java-реализацию в фреймворке Selenium WebDriver.

    Повторное использование кода


    Представьте, что вам понадобилось написать тесты не на какую-то отдельную страницу, а на весь веб-сервис. На его страницах наверняка будут встречаться общие блоки элементов: хедеры, футеры, возможно, какие-то одинаковые формы и т.д. Например, на главной странице Яндекса есть форма поиска, которая сохраняется и при переходе на страницу с поисковой выдачей.

    image


    image

    Она встречается и на других сервисах Яндекса: например, на Яндекс.Авто, Яндекс.Маркете и Яндекс.Работе.

    Форму авторизации тоже можно увидеть не только на главной странице, но и, к примеру, на странице Яндекс.Паспорта или на Яндекс.Маркете. Логика взаимодействия с общими блоками на каждой странице абсолютно одинакова. Но, когда вам понадобится писать page-объекты для этих страниц, вы будете вынуждены в каждом из них продублировать код, реализующий взаимодействие с этими блоками.

    image

    Вы, наверное, уже поняли, к чему я клоню? Да-да, было бы здорово иметь возможность описывать блоки элементов и логику взаимодействия с ними отдельно, и уже из них собирать 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 элементов с учетом расширенного поиска, а ещё список марок автомобилей, блок новостей, блок автомобильных новинок и т.д.

    image

    Если мы напишем 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();
        }
    }

    А так будет выглядеть описание формы для выбора языка в настройках поиска, где есть выпадающий список:

    image
    @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. Попробовать его в деле и посмотреть код можно на GitHub.

    В одном из следующих постов о тестировании в Яндексе мы подробнее расскажем о самом фреймворке. Вы узнаете, какие еще полезные возможности у него есть и как с его помощью удобно тестировать веб-интерфейсы.
    Яндекс
    Как мы делаем Яндекс

    Comments 10

      0
      Недавно сам задумывался о подобном и описал реализацию данного подхода в Codeception.
      codeception.com/10-30-2012/pro-tips-1.html

      С помощью трейтов в PHP 5.4 мы можем подмешивать общие элементы во все страницы.
        +2
        Идея похожа, но реализовали вы данный подход все-таки не полностью.
        У вас page-объекты и блоки используются только для отделения данных, используемых для нахождения элементов, от тестовых сценариев. Это упростит жизнь в случае изменений в верстке, но вам все равно придется в каждом тесте описывать всю цепочку элементарных шагов сценария. То есть для формы авторизации в тестовом сценарии вместо простого authorizationForm.login(login, password) у вас будет: ввести «name» в поле «логин», ввести «passwd» в поле «пароль» и кликнуть на кнопку «войти».

        На самом же деле идея паттерна Page Object стостит в том, чтобы выделить полностью всю логику взаимодействия со страницей отдельно и инкапсулировать её в соответсвующем классе. А мы в этом посте предлагаем идти дальше: разделять страницу на блоки и инкасулировать логику взаимодействия на уровне блоков, чтобы один и тот же блок было удобно переиспользовать во многих page-объектах.
          0
          Спасибо. Учту, дополню )
        0
          +1
          Роман, разница в том, что вы описали, как эти идеи можно реализовать самому, а мы предлагаем решение, которое можно подключить и сразу использовать в своем проекте. Что касается внутреннего устройства, то вы предлагаете делать несколько иначе, чем сделали мы (на мой взгляд, не так удобно).

          К тому же, возможности фреймворка HTML Elements не заканчиваются на описанных здесь. О том, что еще можно делать с его помощью, мы напишем в следующих постах.
          +1
          Спасибо за информацию. Мы тоже пришли к инкапсулированию повторяющихся объектов в классы точно таким же образом, как в вашем фреймворке (правда у нас C# а не Java). Существенно облегчает разработку. За открытость вам большой плюс.
            +1
            Мне это напоминает Wicket. В чем принципиальные отличия?
              +2
              Если вы говорите об Apache Wicket, то это инструмент для совсем других целей. Он предназначен для разработки веб-приложений, а здесь мы говорим о тестировании веб-приложений.
              0
              спасибо.
              стоило упомянуть, что используете jdk7, на с jdk6 не заработало.

            Only users with full accounts can post comments. Log in, please.