Package: pageo
pageoБиблиотека на базе Selenium с расширенным функционалом. Предназначена для автоматической работы с любыми веб-страницами и их элементами.
Вдохновленастатьёй на Хабреи упрощает написание автоматических тестов
с реализацией паттернаPageObject. Ускорьте написание кода и упростите его дальнейшую поддержку.ОписаниеЗачем нужна эта библиотека, если Selenium из коробки предоставляет инструменты для работы со страницами веб-сайтов?
В процессе написания тестов, разработчик регулярно ищет элементы на странице, переходит на другие страницы,
получает атрибуты и т.д. Все оборачивается в ожидания, чтобы избежать внезапных ошибок.
Библиотека предоставляет базовый класс, методы которого позволяют скрыть часть этой логики и комбинируют в себе
различные инструменты базового Selenium, тем самым расширяя функциональность.
Благодаря этому ускоряется написание тестов, их понимание и последующая поддержка.Преимущества перед использованием Selenium из коробки:Простое использование. Чтобы начать пользоваться просто отнаследуйтесь от базового класса.Быстраянастройка тестов. Установите настройки теста, просто передав их в аргументах класса страницы.Классы локаторов. Эти классы скрывают логику поиска элементов и возвращают объектWebDriver.Быстрое использованиеPage Object. Легко создавать тесы используя паттерн Page Object.ОглавлениеУстановкаБыстрый стартДокументация базового классаСоздание объектаМетод find_elementsМетод custom_wait_untilМетод move_to_elementКлассы локаторовclass_name_locatorcss_locatorid_locatorlink_text_locatorname_locatorpartial_link_text_locatortag_name_locatorxpath_locatorДинамическое добавление локаторовИспользование в PageObjectУстановкаУстановите core-page через pip:pipinstallpageoИспользование библиотеки подразумевает, что на вашем компьютере уже имеется chromedriver.
Если нет, то следует ознакомиться сруководством.Быстрый старт:Cоздаем файл для тестируемой страницы и импортируем класс BasePage.Создаем класс страницы и наследуемся от BasePage. Теперь нам доступны всеметодыбазового класса.
Также импортируемкласс локатораIdLocator. С его помощью будет выполняться поиск элемента.# about_page.pyfrompageoimportBasePagefrompageoimportIdLocatorclassAboutPage(BasePage):base_url='https://www.python.org'url_suffix='/about'search_field_element=IdLocator("id-search-field")defis_search_field(self):returnTrueifself.search_field_elementelseFalseТеперь создаем экземпляр классаAboutPageв тесте и передаем некоторыенастройки.# test_about_page.pyfromseleniumimportwebdriverfrompage_object.about_pageimportAboutPagedeftest_search_field_exist():page=AboutPage(driver=webdriver.Chrome(),window_size=(1600,900))search_field_exist=page.is_search_field()assertsearch_field_existПодробнее этот пример описан вразделе про Page Object.Документация базового классаСоздание объектаатрибуты класса:base_url- адрес страницы без относительного пути.Адрес страницы может быть передан без протокола. В таком случае, будет установлен протокол по умолчанию (https).
Если адрес страницы имеет протоколhttp, то его следует указать перед адресом страницы самостоятельно!url_suffix- относительный путь к конкретной странице сайта.Пример определения атрибутов класса:frompageoimportBasePagefrompageoimportIdLocatorclassSomePage(BasePage):base_url='https://some_page.com'url_suffix='/some_page_on_site'some_element=IdLocator("some_id")...Аргументы класса:driver- объектWebDriver. Обязательный аргумент.base_url- адрес страницы без относительного пути. По умолчанию None. Необязательный аргумент.Передавайте URL в аргументах при создании объекта только в том случае, если адрес не был указан в атрибутах класса страницы.Если адрес страницы был указан и в атрибутах класса, и в аргументах при создании объекта, но они отличаются, то будет выброшено исключениеUrlDisparityError.Если URL не был указан ни в атрибутах класса, ни в аргументах при создании объекта, то будет выброшено исключениеUrlAvailabilityError.Адрес страницы может быть передан без протокола. В таком случае, будет установлен протокол по умолчанию (https).
Если адрес страницы имеет протоколhttp, то его следует указать перед адресом страницы!url_suffix- относительный путь к конкретной странице сайта. По умолчанию None. Необязательный аргумент.Передавайте относительный путь в аргументах при создании объекта только в том случае, если относительный путь к конкретной странице не был указан в атрибутах класса страницы.Если относительный путь был указан и в атрибутах класса, и в аргументах при создании объекта, но они отличаются, то будет выброшено исключениеUrlDisparityError.window_size- размер страницы браузера в формате(ширина, высота). По умолчанию установлено значение(1920, 1080). Необязательный аргумент.cookies- список сcookie, которые необходимо передать перед открытием страницы. По умолчаниюNone. Необязательный аргумент.is_open- булев тип, указывающий на необходимость открыть страницу сразу после создания объекта. По умолчаниюTrue.Рекомендуется предварительно создавать свой объектWebDriver.Для примера создадим свой объектWebDriverи передадим ему опции:fromseleniumimportwebdriverfrompage_object.main_pageimportMainPageoptions=webdriver.ChromeOptions()options.add_argument('--headless')driver=webdriver.Chrome(options=options)page=MainPage(driver=driver,base_url='https://google.com')Можно передавать его непосредственно в класс (без предварительного создания):fromseleniumimportwebdriverfrompage_object.main_pageimportMainPagepage=MainPage(driver=webdriver.Chrome(),base_url='https://google.com')Метод find_elementsМетодfind_elementsвозвращает элементы, которые были найденыклассом локатора, в виде списка.
Будет возвращен список, даже если был найден только один элемент.Принимает следующие аргументы:locator - строка, которая содержит имя переменной локатора, хранящей веб-элемент.frompage_object.main_pageimportMainPagedeftest_some_element():page=MainPage(base_url='https://google.com',url_suffix='/doodles')element=page.find_elements('some_locator_name')...Метод custom_wait_untilМетодcustom_wait_untilпозволяет задавать ожидания на основе пользовательских условий. В случае, когда условие
ожидания, которое нам нужно, не предусмотрено Selenium, пользователь может сам задать условие ожидания на основе какой-либо функции.Принимает следующие аргументы:func_condition - функция предикат, в которой задано условие ожидания.duration - время в секундах, в течение которого будет выполняться ожидание.frompageoimportIdLocatorfrompage_object.main_pageimportMainPagedeftest_some_element():page=MainPage(base_url='https://google.com',url_suffix='/doodles')link_button=IdLocator('about-link')link_button.click()page.custom_wait_until(lambdadriver:driver.current_url!=page.url)...Метод move_to_elementМетодmove_to_elementимитирует наведение мыши на элемент. Комбинирует внутри себя создание объектаActionChains,
наведение на элемент и выполнение действия (метод perform).Метод принимает один аргумент:element - объектWebElement, на который нужно выполнить наведение мыши.frompageoimportIdLocatorfrompage_object.main_pageimportMainPagedeftest_some_element():page=MainPage(base_url='https://google.com',url_suffix='/doodles')about_button=IdLocator('about-link')page.move_to_element(about_button)...Классы локаторовВспомогательные классы локаторов дляBasePage, инкапсулирующие логику поиска элементов по локаторам.
Каждый класс наследуется от абстрактного классаAbstractLocator.
Использование классов локаторов позволяет создавать их экземпляры в классах страницы как атрибуты класса. Если селектор изменится
или нужно изменить стратегию поиска элемента, то достаточно просто изменить селектор элемента в аргументах класса или сам класс-стратегию.Каждый класс локатора принимает следующие аргументы:selector- селектор искомого элемента.Обязательный аргумент.is_many- флаг, указывающий на множественность элементов.ЕслиFalse, то будет возвращен один найденный элемент (первый элемент, который соответствует локатору).ЕслиTrue, то будет возвращен список всех найденных элементов.timeout- время в секундах, в течение которого будет выполняться ожидание. По умолчанию 5 секунд.Каждый класс локатора возвращает объектWebElement.Пример использования класса локатора для поиска одного элемента:frompageoimportBasePagefrompageoimportClassNameLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=ClassNameLocator("cardlink",timeout=3)Если элементов много и нужны все:frompageoimportBasePagefrompageoimportClassNameLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_elements=ClassNameLocator("cardlink",is_many=True,timeout=6)Далее подробней описаны все доступные классы локаторов.class_name_locatorКласс локатора, выполняющий поиск на основе имени класса элемента.frompageoimportBasePagefrompageoimportClassNameLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=ClassNameLocator("cardlink")css_locatorКласс локатора, выполняющий поиск на основе css-локатора элемента.frompageoimportBasePagefrompageoimportCSSLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=CSSLocator("#latest-title")id_locatorКласс локатора, выполняющий поиск на основе атрибута id у тега элемента.frompageoimportBasePagefrompageoimportIdLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=IdLocator("highlight")link_text_locatorКласс локатора, выполняющий поиск на основе текста внутри ссылки элемента.frompageoimportBasePagefrompageoimportLinkTextLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=LinkTextLocator("Подробности")name_locatorКласс локатора, выполняющий поиск на основе атрибута name у тега элемента.frompageoimportBasePagefrompageoimportNameLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=NameLocator("q")partial_link_text_locatorКласс локатора, выполняющий поиск на основе вхождения текста внутри ссылки элемента.frompageoimportBasePagefrompageoimportPartialLinkTextLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=PartialLinkTextLocator("Подробности")tag_name_locatorКласс локатора, выполняющий поиск на основе имени тега элемента.frompageoimportBasePagefrompageoimportTagNameLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=TagNameLocator("input")xpath_locatorКласс локатора, выполняющий поиск на основе пути XPath до элемента.frompageoimportBasePagefrompageoimportXPATHLocatorclassDoodlesPage(BasePage):base_url="http://google.com"url_suffix="doodles"some_element=XPATHLocator("//*[@id='searchinput'']")Динамическое добавление локаторовМожет возникнуть ситуация, когда необходимо добавить новые локаторы в класс в самом тесте или в методах класса.Например, нам нужно автоматически пройти тестирование. Варианты ответа на вопрос расположены вразброс:frompageoimportBasePagefrompageoimportXPATHLocatorclassExamPage(BasePage):passdefpass_exam(self):for_inrange(10):self.add_locators(answer_text=XPATHLocator(f"//*[text()='{answer}']"),)self.answer_text.click()Такой способ добавления локаторов в класс страницы может быть полезен, когда нужный элемент может меняться и из-за этого необходимо изменять локатор.Использование вPageObjectИспользование паттернаPageObjectпозволяет упростить написание, поддержку
и масштабирование тестов. Базовый класс может в этом помочь.Допустим, нужно протестировать сайтhttps://www.python.org/. Он содержит множество страниц и элементов.
Паттерн подразумевает, что каждая страница будет представлена как отдельный класс с методами страницы.В таком случае, структура проекта может выглядеть следующим образом:project/
├── page_object/
│   ├── __init__.py
│   ├── main_page.py
│   └── about_page.py
├── tests/
│   ├── __init__.py
│   ├── test_main_page.py
│   └── test_about_page.py
├── ...
└── ...Рассмотрим файлabout_page.py. Он должен описывать методы, содержащие поиск элементов и взаимодействие с ними.В нашем случае, протестируем наличие поля для поиска по сайту.
Для поиска элемента на сайте будет использоватьсяIdLocator.# about_page.pyfrompageoimportBasePagefrompageoimportIdLocatorclassAboutPage(BasePage):# Определяем в атрибутах класса базовый URL и относительный путь к конкретной странице.base_url='https://www.python.org'url_suffix='/about'# Определяем в атрибутах класса переменную, которая будет содержать искомый элемент (объект типа WebElement).search_field_element=IdLocator("id-search-field")# Описываем метод, содержащий сценарий, который будет использоваться непосредственно в кейсе.defis_search_field(self):returnTrueifself.search_field_elementelseFalseПерейдем в файлtest_about_page.py. Здесь мы создаем тест-кейс, в котором проверяем сценарий, описанный в классе страницы.# test_about_page.pyfromseleniumimportwebdriverfrompage_object.about_pageimportAboutPage# Создаем тест, который проверяет наличие поля поиска по сайту.deftest_search_field_exist():# Создаем экземпляр класса страницы. (base_url и url_suffix были определены в атрибутах класса, так что передавать в аргументах их не нужно.)page=AboutPage(driver=webdriver.Chrome())search_field_exist=page.is_search_field()assertsearch_field_existБлагодаря такой структуре проекта и использованию паттерна, мы можем легко поддерживать и писать гибкие сценарии.
Даже при значительных изменениях тестируемой страницы, исправление тестов не займет много времени.