Статьи

Моделирование веб-страниц с шаблоном объекта страницы

Объекты страницы

Мы взаимодействуем с веб-страницами каждый день. На низком уровне, нажимая на различные элементы HTML или вводя текст в текстовые формы, используя устройства ввода. Какова цель более высокого уровня делать эти вещи? Он пытается что-то сделать . Завершение поиска в Google, отправка контактной формы или оценка чего-либо. Шаблон Page Object — это отличный объектно-ориентированный способ поддержания чистоты кода при выполнении задач более высокого уровня по мере роста вашего приложения.

Основы шаблона объекта Page

Зачем использовать шаблон Page Object? Два слова: повторное использование кода. Позволь мне объяснить.

Предположим, вы создаете приложение, которое перейдет в поисковую систему и выполнит поиск по определенной фразе. Для этого вам необходимо:

  • Перейти на веб-страницу
  • Найдите элемент HTML, где вы можете ввести текст и ввести ключевое слово (текстовое поле)
  • Найдите кнопку поиска и нажмите ее

Если вы знакомы с Watir-Webdriver / Selenium, вы знаете, что это легко сделать с помощью трех строк. Если вы не знакомы с Watir / Selenium, прочитайте мою статью Watir, а затем вернитесь к этой статье. Я жду.

Предположим, вам необходимо выполнить каждое из этих трех действий несколько раз в своем коде. Что ты можешь сделать? Конечно, вы можете обернуть свой код в метод, и все готово, верно? Но что, если ваше приложение становится более сложным? Предположим, что вместо нажатия кнопки «Поиск» вы хотите нажать «Мне повезет» после ввода текста. Или, по какой-то причине, сначала дважды нажмите кнопку «Поиск», а затем введите текст и снова нажмите «Поиск», повторив это несколько раз в своем коде.

Вы можете получить несколько методов, содержащих несколько ссылок на некоторые элементы HTML. Когда элемент HTML изменяется (из-за редизайна страницы, он уже не в классе X, а в Y), вам нужно будет внести изменения в нескольких местах кода. Теперь представьте что-то в десять раз более сложное, чем страница поиска, содержащая 20 различных элементов, которые вы можете комбинировать 7 различными способами. Все становится действительно грязно, очень быстро.

Шаблон Page Object решает эту проблему, помогая моделировать одну веб-страницу как класс, предоставляя ее «сервисы» (поиск по ключевому слову, поиск с использованием «I’m Feeling Lucky») в качестве общедоступных методов. На веб-странице электронной почты могут быть методы составления электронной почты, чтения входящих сообщений или сохранения сообщения в виде черновика. Вы не хотите беспокоиться, как это происходит под капотом.

Вы можете обойтись без Watir / Selenium, если вы имеете дело с простым приложением, и использование шаблона Page Object может оказаться излишним (я использую простой пример в этой статье, чтобы проиллюстрировать, как работает весь драгоценный камень). Такие шаблоны, как Page Object не были изобретены для этой цели. Они были созданы для того, чтобы ваш код стал более управляемым.

страница-объект: рубиновый камень

Драгоценный камень объекта страницы является очень простой реализацией этого шаблона. Чтобы установить его , просто наберите gem install page-object и все готово. Убедитесь, что у вас также установлен Watir-Webdriver, потому что мы будем использовать его для этой статьи.

Ранее мы упоминали, что шаблон Page Object позволяет вам моделировать веб-страницу, выставляя ее «сервисы» как публичные методы. Если вы хотите смоделировать конкретную веб-страницу, используя гем Page Object, вы пройдете следующие 3 шага:

  • Создайте страницу , определив класс и включив PageObject в качестве модуля.
  • Опишите страницу , указав элементы HTML, которые вы будете использовать для взаимодействия с ней.
  • Используйте страницу. инициализируя класс как объект и вызывая его методы.

Создать страницу поиска

Давайте реализуем эти 3 шага, используя простую страницу поиска. Для начала мы создадим класс и включим в него модуль Page Object. Попробуйте запустить этот код, чтобы увидеть, все ли работает.

 require 'page-object' class SearchPage include PageObject # some code to declare the HTML elements end 

Какой смысл PageObject модуль PageObject ? Каждый модуль предоставляет методы, и это не исключение. Он также будет включать в себя магию метапрограммирования, которую мы рассмотрим позже.

Опишите единственную страницу поиска

В 2006 году сеть была простой. Эта страница поиска Yahoo не стала исключением. Давайте попробуем описать его, дав ему имя и указав на него местоположение.

Прежде чем начать использовать объект страницы на веб-странице, спросите себя: «Что я пытаюсь сделать на этой странице? «, А затем: какие элементы HTML мне понадобятся для выполнения этих задач?

Мы начинаем с простой страницы поиска, просто используя ее для поиска определенного ключевого слова. Для этого нам нужно определить текстовое поле и кнопку поиска.

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

 class SearchPage include PageObject text_field(:search_box, id: 'yschsp') button(:search_button, class: 'ygbt') end 

text_field и button — это просто вызовы метода. При определении HTML-элемента в объекте Page (с помощью методов, подобных этим двум) необходимо сделать две основные вещи:

  • Назовите элемент (мы назвали наши два элемента search_box и search_button , соответственно.) Это делается путем передачи имени в качестве символа для первого аргумента.

  • Предоставьте инструкции о том, как его найти. Что уникально идентифицирует этот элемент на странице? Часто это всего лишь одна вещь, такая как id ( :yschsp ) или class CSS ( ygbt ). Но что, если это не так? Предположим, на странице был другой элемент, который имел тот же класс, что и кнопка, и не имел идентификатора. В этом случае укажите другой атрибут, чтобы было понятно, что искать.

Вот код, который я бы использовал:

 button(:search_button, class: 'ygbt', value: 'Search the Web') 

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

Элементы определены, что теперь?

Здесь происходит волшебство! После моделирования / определения нашего текстового поля и кнопки мы создали пять различных методов. Да, пять из них! Например, после ввода этого:

 text_field(:search_box, id: 'yschsp') 

Page Object создаст три отдельных метода:

 search_box # returns the value in the text field search_box= # sets the value of the text field search_box_element # returns a reference to the actual HTML element 

Весь смысл определения и моделирования элементов HTML с помощью вызовов text_field и button заключается в создании вышеуказанных методов. Эти недавно созданные методы позволяют нам работать с фактическим элементом и манипулировать им.

Для получения дополнительной информации о том, какие методы создаются при определении элемента, обратитесь к этой вики-странице . В следующем разделе мы немного поиграем с ними.

Используйте свой объект страницы

Для работы Page Object необходим экземпляр браузера Watir / Selenium. Gem страницы-объекта не дает инструкции браузеру напрямую, для получения дополнительной информации см. Эту статью ). Я буду использовать Watir через эту статью. Давайте сначала определим новый экземпляр браузера:

 require 'watir-webdriver' browser = Watir::Browser.new :chrome 

Мы забыли добавить одну важную вещь в определение нашей SearchPage . Можете ли вы угадать, какой? Конечно, у нас есть все элементы, определенные для управления страницей. Но какая именно страница? Где находится эта страница? Чтобы сообщить объекту страницы URL-адрес, по которому можно найти страницу, добавьте метод page_url непосредственно внутри класса SearchPage с аргументом, указывающим фактический URL-адрес:

 page_url('https://web.archive.org/web/20060204194204/http://search.yahoo.com/') 

Если вы не уверены, как все это происходит вместе, полный код приведен ниже.

Теперь у нас есть все необходимые элементы, чтобы начать! Чтобы начать использовать класс Page Object, просто создайте его экземпляр и укажите экземпляр браузера, который вы хотите использовать в качестве параметра:

 search_page = SearchPage.new(browser) 

Вы не можете использовать свой класс Page Object, не подключив его к браузеру. Должно быть, на что-то воздействовать.

Вот полный код. Я добавил несколько методов sleep чтобы вы могли четко видеть, что происходит. Посмотрите, понимаете ли вы это полностью и попробуйте запустить его на своем компьютере, прежде чем продолжить эту статью:

 require 'page-object' require 'watir-webdriver' class SearchPage include PageObject page_url('https://web.archive.org/web/20060204194204/http://search.yahoo.com/') text_field(:search_box, id: 'yschsp') button(:search_button, class: 'ygbt') end browser = Watir::Browser.new :chrome search_page = SearchPage.new(browser) ; sleep 5 search_page.goto; sleep 5 search_page.search_box = 'I love SitePoint!'; sleep 5 search_page.search_button 

Поскольку мы имеем дело со страницей Archive.org, вы не получите фактические результаты поиска, когда ваш скрипт нажмет кнопку «Поиск в Интернете». Я рекомендую вам изменить его для оригинального [https://search.yahoo.com/] и заставить его работать после окончания этой статьи.

Вы уже знаете, откуда появились методы search_box= и search_button , но как насчет goto ? goto также является сгенерированным методом. Он был создан, когда мы вызвали page_url чтобы указать URL.

text_box , page_url и button называются аксессорами . Как только вы вызываете их непосредственно внутри своего класса, page-object автоматически создает соответствующие методы, которые вы можете использовать при создании объекта, как мы видели выше.

Использование page_url создает метод goto который ведет вас на страницу. Использование text_area создает четыре разных метода . Определение кнопки создает три .

Однако в большинстве случаев вы захотите сразу goto на страницу при создании экземпляра объекта Page ( search_page ). Это легко сделать, добавив еще один аргумент в конструктор:

 search_page = SearchPage.new(browser, true) 

Если первым аргументом был экземпляр браузера, то вторым является необходимость немедленного посещения страницы, указанной в page_url (путем вызова goto для вас в фоновом режиме).

Группировка к общей цели

Пока что я пытаюсь продемонстрировать, как работает объект Page Object. Код выше, на самом деле, можно было бы легко написать на Watir, и он был бы примерно такой же длины. Волшебство этого шаблона приходит в себя, когда мы группируем все вместе в методе для достижения общей цели (поиск ключевого слова). Давайте добавим этот метод в определение класса SearchPage :

 def look_for(keyword) self.search_box = keyword; sleep 5 self.search_button end 

А затем сделайте:

 search_page = SearchPage.new(browser, true) search_page.look_for('I love SitePoint') 

Это намного чище, чем то, что предлагает Watir. Примечание о том, почему стоит включить self в look_for : многие методы, сгенерированные объектом страницы, являются методами типа searchbox = , такими как searchbox = . При вызове этих типов методов внутри метода экземпляра, такого как look_for , если вы не добавите self перед ними, Ruby будет интерпретировать их как локальные переменные, а не как вызовы методов. Например, попробуйте предсказать, что этот код будет выводить на консоль:

 def hello=(arg) # assignment method puts arg end def hi(arg) # ordinary method puts arg end def integrate hello = 'article' # it's supposed to call the assignment method hi('blog') self.hello = 'ruby' end integrate 

Вывод будет «блог» и «рубин», «статья» не будет печататься вообще.

Э-э-э … Наша страница поиска изменилась

Давайте перенесем нашу поисковую страницу на 2013 год, когда в Yahoo Search произошли серьезные изменения. Вот URL . Поместите эту ссылку в качестве нового аргумента для page_url и попробуйте запустить свой код. Вы должны получить это:

 unable to locate element, using {:class=>"ygbt", :tag_name=>"button"} (Watir::Exception::UnknownObjectException) 

С новым дизайном Yahoo изменила свою кнопку поиска, больше не идентифицируя ее с ygbt качестве имени класса. Класс теперь называется sbb-sd . Чтобы наш код снова заработал, просто измените строку, в которой вы определяете кнопку в своем классе:

 button(:search_button, class: 'sbb-sd') 

Я хочу, чтобы вы представили, каким был бы этот процесс, если бы вы не использовали шаблон Page Object. Что делать, если у вас было 5 обычных методов, каждый из которых использовал эту кнопку, и каждый из них использовал эту кнопку для определенной цели. Вы должны изменить свой код 5 раз! Конечно, вы можете реализовать свой собственный шаблон, но посмотрите, насколько сложнее это сделать вручную . Кроме того, это использует Watir, самый простой из возможных инструментов автоматизации с использованием Ruby, одного из языков с самым чистым синтаксисом. Представьте себе использование Selenium и Java для реализации вашего собственного шаблона Page Object.

Вот полный код, который включает новый аргумент для page_url и метод look_for :

 require 'page-object' require 'watir-webdriver' class SearchPage include PageObject page_url('https://web.archive.org/web/20130307071919/http://search.yahoo.com/') def look_for(keyword) self.search_box = keyword; sleep 5 self.search_button end text_field(:search_box, id: 'yschsp') button(:search_button, class: 'sbb-sd') end browser = Watir::Browser.new :chrome search_page = SearchPage.new(browser, true) search_page.look_for('I love SitePoint') 

Советы по использованию объекта Page

Вот несколько полезных советов, которые могут пригодиться при использовании этого драгоценного камня:

Доступ к экземпляру браузера

Вы можете получить доступ к экземпляру браузера из вашего класса PageObject либо через @browser либо используя его browser атрибутов getter, например:

 def look_for(keyword) self.search_box = keyword; sleep 5 self.search_button puts browser.text end 

Навигация по браузеру без экземпляра браузера

У вас есть множество способов перейти вперед, назад, обновить, очистить куки и т. Д., Используя экземпляр браузера. Я добавил 2-секундную паузу между каждым действием, чтобы вы могли видеть, что происходит более четко:

 def look_for(keyword) self.search_box = keyword self.search_button back; sleep 2 forward; sleep 2 clear_cookies; sleep 2; puts 'Cookies cleared!' puts current_url; sleep 2 puts element_with_focus; sleep 2 refresh; sleep 2 save_screenshot('screenshot.jpeg'); sleep 2 end 

Обработка оповещений

Page Object позволяет пропустить окно оповещения, получая только его значение. Возьмите эту страницу , например, и попробуйте нажать кнопку «Попробуйте сейчас». Вы должны получить «Привет из JavaScript!». Теперь попробуйте этот код:

 class AlertPage include PageObject page_url('https://web.archive.org/web/20150618033134/http://javascripter.net/faq/alert.htm') def get_alert_text message = alert do self.try_it_now end p message end button(:try_it_now, value: 'Try it now') end browser = Watir::Browser.new :chrome alert_page = AlertPage.new(browser, true) alert_page.get_alert_text 

Обратите внимание, как окно предупреждения не появляется вообще! Чтобы сделать то же самое с подтверждением / запросами, см. Эту статью .

У Watir есть довольно хорошие методы для работы с окнами оповещений и Ajax, которые я считаю более удобными.

После инициализации

Не используйте initialize если вы хотите, чтобы какой-то код выполнялся при создании вашего объекта. PageObject использует этот метод, и все сломается, если вы попытаетесь перезаписать его. Вместо этого используйте initialize_page . Попробуйте добавить этот метод в свой класс SearchPage чтобы понять, что я имею в виду:

 def initialize_page puts 'I am being initialized!' end 

Как узнать больше

Вот более полезные ресурсы, где вы можете узнать о Page Object:

Попробуйте использовать шаблон Page Object в следующий раз, когда вам потребуется использовать веб-страницу в Ruby. Я думаю, что это сэкономит вам много времени и обострения.