Примеры использования других дизайн-паттернов при работе с page objects

page-object
design-patterns
Теги: #<Tag:0x00007fedbc5a5d28> #<Tag:0x00007fedbc5a5b48>

(Tatyana Durova) #1

Заинтересовалась шаблонами проектирования, но многие из примеров в описании паттернов мне трудно представить при автоматизации (Page component object).

Может есть какие-то темы-статьи, где раскрывается эта тема?

Например Singleton часто сравнивают с "телефонной станцией" или бухгалтерии в фирме(типа он только одна и всегда помнит, что с ней было) - в Pageobject такой паттерн удобно использовать при запуске только одного экземпляра браузера. Пул одиночек? Мне уже сложнее представить, но возможно, это несколько независимых экземпляров браузера ля параллельного запуска тестов..

Фабрика - создает страницы, но опять же, какая конкретно? Абстрактная? Что мог бы делать строитель? Фабричный метод? Стратегия? Компоновщик? И тд, применитьльно к page object.

Также интересно глянуть на uml диаграмму именно page-object проектов..

Нашла хорошие примеры real world кода паттернов на http://www.dofactory.com/net/strategy-design-pattern, но даже по ним написать какие то примеры real world кода при page object я не могу..

Еще все статьи про page object мне кажутся статьями первого уровня, больше ознакомительными, если есть какие то книги или более глубокие статьи, буду очень рада ссылкам..


(Goshko Nazar) #2

Начнем с того что page object - это уже некий паттерн. Второе, паттерны, уместны тогда, когда система, которую они описывают, является прозрачной и однозначной для описания, а не "живой и не состоявшейся".
В автотестах, которые часто приходится менять, жесткое завязывание чревато переделкой всего приложения.

Не знаю как кто делает, у меня стратегия такая:
1. выносим abstract basepage - общая страница, которая базовая для всех страниц, она не обязательно может иметь конечный функционал, обычно в ней реализованы базовые методы типа, логина, передача всех необходимых параметров и путей, для других классов, передача инстанса драйвера.
2. разделяем страницы на страницы, и сортируем элементы. Опять таки, глобальные элементы (хедер/футер, возможно навигация по сайту) выносятся или в отдельные классы как фрагмент страницы, или реализуются в base page. Не уникальные выносятся в файл/модуль страниц, откуда потом импортируются.
3. Страница - новый класс, функциональность - метод. Функциональность возвращает страницу? - новый класс. И далее Вы просто набираете нужные страницы из доступных элементов

Тесты. любой из тестов принимает на вход фикстуру App, которая возвращает приложение с драйвером и всеми параметрами - в его контексте вы выполняете нужные вам тесты. Никто вам не мешает вызвать несколько инстансов приложения - хоть 100 (к вопросу о параллельности), зависит только от реализации возвращения Арр - контекста. тесты соответственно и выглядят соответствующе.

page = app.home.select_menu('Профиль')
assert page.get_name == 'Вася'

Или что лучше, подтянуть параметры пользователя извне - юаза, ini файл. В Арр определить класс который возвращает инфу пользователя из файла, и тогда вообще мед:

page = app.home.select_menu('Профиль')
assert page.get_name == app.data.name

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

По вопросу: на Вики описаны ВСЕ паттерны программирования с примерами кода на многих языках. Открыв - я не увидел там чего то сверхестественного, ибо, если не понимаешь как оно работает, и заче это? - значит ты пока не готов, тебе это не нужно.


(Sergey Korol) #3

Большинство статей по паттернам - весьма абстрактны, это верно. Для того, чтобы понять, как их применять именно в test automation, не достаточно лишь прочитать определения и парочку примеров.

Вся проблема в том, что будучи начинающим программистом / автоматизатором, у вас пока еще не сформировано абстрактное видение различных архитектурных нюансов. На данный момент, читая чей-то код, вы можете понимать, к примеру, лишь синтаксическую составляющую, но не иметь ни малейшего представления об архитектурной. Это сродни тому, как в универе мы могли зубрить различные мат. формулы, но абсолютно не понимать их практического жизненного применения.

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

Вы отталкиваетесь лишь от своего небольшого опыта, потому, увы, применимость многих вещей вам пока еще может быть совершенно не понятной. Паттерны - не исключение.

Касательно применимости в целом, по своему опыту могу заключить следующее (прим. - говорю с позиции Java automation engineer):

  • Часть всеми известных паттернов уже стала совершенно неактуальной, ввиду грандиозных апдейтов 8й версии. Некоторые паттерны теперь имеют совершенно другой вид (в большинстве - упрощенный).
  • Основная часть паттернов в чистом виде больше применима для продуктовой разработки. Для автомейшена нередко приходилось немного их модифицировать, в зависимости от контекста.
  • Не следует пытаться рефакторить код, натыкивая паттернов везде, где не попадя. Подобные бездумные шаги могут привести к абсолютно ненужному усложнению кода и костыльным решениям.

Какие паттерны наиболее применимы в автоматизации? Однозначного ответа нет, ибо, как я уже заметил выше, многое зависит от контекста. Для web-automation из актуального я бы выделил следующие:

  • PageObject (не нуждается в представлении).
  • Factory берет на себя задачи инициализации сложных однотипных объектов, отдавая вам лишь результат (pages / drivers).
  • Facade может объединять различные компоненты одной большой системы, предоставляя пользователю более простую, интуитивно понятную единую точку доступа. В частном случае, может быть реализован в форме BasePage.
  • Decorator может, к примеру, сокрывать в себе логику неявной инициализации кастомных локаторов, расширяя уже существующий механизм, без прямого структурного вмешательства.
  • DAO предоставляет простой user-fiendly доступ к вашим entities, отделяя low-level DB API от высокоуровневых сервисов. В автомейшене от него было больше overhead, чем пользы. Потому пришлось изобретать некий generic DAO в связке с кастомным DataProvider. От точечных реализаций отказался со временем.
  • Singleton служит для предоставления единственного экземпляра объекта на протяжении всего ЖЦ исполняемого кода (или его изолированного участка). Может быть применим допустим к драйверу (если параллелизация по thread не предполагается), или к DB connection (чтобы каждый раз его не переоткрывать).
  • Builder применим для построения сложных объектов. В чистом виде мне не очень по душе. Для себя выработал некую модификацию. Применяю, когда есть entity с большим числом кастомных филдов, часть из которых могут быть опциональными (к примеру, Jira сущности).

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

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


(Tatyana Durova) #4

что вы имели в виду? когда метод субстраницы возвращают либо эту же субстраницу, либо другую?

А если делать поле в классе MainPage поле List mainMenu, в который делать mainMenu.add(blockMenu1) и тд, или это не очень? Или лучше BasePage mainMenu = new MainMenu, и уже в классе MainMenu агрегировать в полях blockMenu1, blockMenu2?


(Tatyana Durova) #5

Я правильно думаю, что им можно делать динамическую реализацию объекта класса MainPage?


(Sergey Korol) #6

Что вы понимаете под динамической реализацией?


(Tatyana Durova) #7

Если честно я это снесла, сделала все попроще пока.
mainpage.performFooter(); - имелось в виду invoke, инициализация (все шаги для появления этого блока всеми способами).


(Oleksandr Khotemskyi) #8

А что делают методы mainpage.performHeader() , mainpage.performMenu(); , mainpage.performBody(); , mainpage.performFooter(); ?

  • у вас где-то потерялась одна открывающаяя скобка -
    //где headerBehaviour - интерфейс с методом header
    } - нет открывающей.