Всем доброго времени суток. Давайте попробуем позаимствовать у UX/UI-дизайнеров методологию проектирования дизайн систем и применить ее в автоматизации тестирования. Начнем разбор с теоретической части. Если вы знаете что такое Atomic Design и Page Object, то можно смело пропускать первые 2 блока.


Что такое Atomic Design.

Методология Брэда Фроста для проектирования дизайн-систем. Она делит интерфейс на пять уровней абстракции — от базовых элементов (атомов) до целых страниц, делая компоненты гибкими и масштабируемыми.

Полный список абстракций:

  • Атомы(Atoms)

  • Молекулы(Molecules)

  • Организмы(Organisms)

  • Шаблоны(Templates)

  • Страницы(Pages)

Какие основные преимущества:

  • Чёткая организация. Каждый компонент имеет своё место в иерархии: атом, молекула, организм, шаблон или страница.

  • Переиспользование. Атом или молекулу достаточно описать один раз. Затем их можно повторно использовать в различных местах приложения.

  • Согласованность. Все UI-элементы объединены единым визуальным стилем и принципами. Изменение в атоме (например, цвета) автоматически применяется ко всем молекулам и организмам, где он используется.

  • Ускорение разработки. Из готовых наборов «кирпичиков» (атомов, молекул, организмов), можно быстро собирать новые страницы и шаблоны.

Ну и куда же без недостатков:

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

  • Размытая граница между уровнями. В реальных проектах порой сложно определить, что относить к молекулам, а что к организмам.

  • Отсутствие бизнес-ориентированности. Подход, в первую очередь, решает вопросы UI. Организацию бизнес-логики в проекте он не затрагивает.

Более детально про Atomic Design можно прочитать книгу автора тут.


Что есть Page Object.

Page Object Model («объектная модель страницы»), также известная как POM, — это шаблон проектирования в Selenium, который создаёт хранилище объектов для управления веб-элементами. Он помогает сократить дублирование кода и упрощает поддержку тест-кейсов.

Взято с просторов хабра.(не реклама). Теоретической части для ознакомления достаточно.

Так же есть еще варианты, когда локаторы/селекторы выносятся в отдельный класс/enum, но сути это не меняет.


Что есть общего у Page Object и Atomic Design.

На первый взгляд кажется что общего маловато, но это не совсем так.

Давайте сравним, что есть в Page Object и чего нет в Atomic Design:

Page Object

Atomic Design

Атомарная единица

локатор, селектор и т.д.

Элемент интерфейса

Взаимодействие с атомарной единицей

Вспомогательные классы для стандартизации работы с типовыми элементам(полями ввода, кнопками и т.д.)

Компоненты, собранные из атомов.

Работа в контексте страницы

Page классы, в которых мы описываем методы работы.

Страница, которая "собирается" из всех пяти абстрактных групп

Судя по тому, что мы имеем в таблице, Atomic Design содержит в себе все то, что есть в Page Object.


Почему именно Atomic Design?

Так как мы говорим про паттерн который применяется для написания UI тестов, почему бы не заимствовать то, чем уже пользуются UX/UI-дизайнеры и фронтенд-разработчики?

Давайте посмотрим из чего состоит самый простой интерфейс страницы:

  • Шапка (header)

  • Верхнее меню

  • Тело (body)

  • Боковое меню (sidebar)

  • Подвал (footer)

А теперь давайте ответим на вопрос: "А что из перечисленного выше списка, чаще всего меняется при переходе на другую страницу?". В 90% случаев будет первым назовут: "Тело(body)". И если нам необходимо использовать верхнее меню, боковое меню или любой другой статичный блок, то вписаться в концепцию Page Object'a уже не особо получается.


Недостатки Page Object.

На основе личного опыта, выделил несколько недостатков.

  1. Невозможность повторного использования одних и тех же элементов, на разных страницах.

  2. Страницы, с большим количеством степов, превращаются в огромную портянку и поддерживать ее весьма муторно.

  3. Взаимодействие со статическими блоками страницы, превращаются в попытку усидеть на двух стульях(соблюдение паттерна и при этом сделать нормальный читаемый page/test)

  4. Сложность в понимании локаторов/селекторов элемента, когда их становится очень много.(они могут быть вынесены в отдельные переменные или нет).

  5. Дублирование локаторов/селекторов и методов, при наличии динамических форм.(как следствие из первого пункта)

Если пункт 2 не нуждается в пояснении(как мне кажется), то с остальными пунктами примеры будут не лишними.

Начнем с пункта 1.

Допустим у нас есть кнопка "Save" и кнопка "Cancel", которая применяется во всех формах, где присутствует редактирование.

String saveButton = "//*[@name='save']";
String cancelButton = "//*[@name='cancel']";

@Step
public void clickSave() {
  elementIsVisible(saveButton);
  clickOnElementByXpath(saveButton);
}

@Step
public void clickCancel() {
  elementIsVisible(cancelButton);
  clickOnElementByXpath(cancelButton);
}

То есть, у нас получается так, что каждая страница где у нас есть создание/редактирование будет иметь вот такие одинаковые куски кода. А если мы еще добавим сообщение об успешном сохранении, то это еще + 50% от того кода, что выше. А если добавить такой момент, что name=‘save’ решили поменять на name=‘apply’ - прелесть. Осталось понять, сколько страниц надо поправить. И это только самые очевидные вещи.

Пункт 3.

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

@Test
pubcli void navigateToUsersPage() {
  mainPage
    .checkMainPageIsOpened;
  sidebar
    .openUsersPage;
  usersPage
    .checkUsersPageIsOpened;
}

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

Теперь п4.

В этом пункте, на самом деле, есть 2 вариации.

Локаторы/селекторы вынесены в отдельные переменные. Здесь ключевую проблему играет нейминг переменных, когда их у тебя 50 и более. Попробуй придумать им нейминг так, чтобы через пол года сходу вспомнить что они значат. Если вы так можете - поздравляю. У вас такой проблемы нет. Но у большинства они есть.

Локаторы/селекторы внутри степов. Давайте сразу с примера:

@Step
public void clickSaveAddressButton() {
  clickOnElementByXpath("//button[@name='Ok']")
 }

@Step
public void clickSaveAppointmentsButton() {
  clickOnElementByXpath("//button[@name='Approve']")
 }

В этом примере у нас есть всего 2 кнопки. Только вот ответить на вопрос: "Относятся ли кнопки к одной форме редактирования или нет?" - нет, пока не посмотришь в тесты, ну или в код страницы(Elements в dev-tools). А если у нас степов 50+ ?

И напоследок, пункт 5.

Эту боль знает каждый, кому приходилось автоматизировать формы, анкеты, заявления в которых есть конфигурация.

Представим, у нас есть форма заявления на кредитную карту. В зависимости от того, является наш клиент зарплатным или нет, форма может отличаться. А если, допустить что есть еще короткая и полная форма заявления. Если смотреть с точки зрения Page Object, то нам приходится делать разные формы в зависимости от 2 параметров:

  • isSalary=true/false

  • isShortForm=true/false

То есть, как минимум, уже 4 страницы надо описать. Некоторые локаторы/селекторы и методы будут гарантировано дублироваться. А если добавляем еще возможность добавления/удаления форм через админку - уже звучит больно. Да, есть мысли что можно вынести общие локаторы или сделать одно хранилище в виде класса/enum, но будет каша. Так же можно выносить общие методы во вспомогательные классы, но это тоже будет каша, но из методов. Приходилось наблюдать такие решения. Да, оно работает. Правда, поддерживать такое - неприятно.


Ключевые сущности в тестовом фреймворке при использовании Atomic Design.

Давайте сразу определимся что есть что:

Атом

Это наши локаторы, селекторы и все чем мы можем идентифицировать элемент на странице

Молекула

Это компонент, использующий наши атомы, чтобы взаимодействовать с UI.

Организм

Это форма "в сборе" из атомов и молекул.

Шаблон

Это "каркас" страницы с его статическими блоками.

Страница

Это все перечисленное выше, собранное в единое целое.

Атом. Для их хранения проще всего использовать отдельный класс. Можно использовать и енам, если нужно иметь краткое описание поля. Такой подход весьма неплохо себя показал, если специфика продукта весьма сложная, а обмазывать комментариями каждый локатор/селектор не хочется.

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

public static void fillInput(String xpath, String text) {
  page.locator(xpath).fill(text);
}

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

public static void searchElementAndSelectFromDropdown(String xpath, String text) {
  clickOnElement(xpath);
  fillInput(text)
  clickOnElementFromListByText(text);
}

Можно возразить, что молекула состоящая из молекул и как-то не вписывается в эту концепцию. И да, вы будете правы. Но если специально дублировать код, чтобы вписаться в концепцию "atomic design" - выглядит это еще хуже.(а такого нам не надо).

Организм. Это готовая форма для использования. Пример:

public void fillPersonalData(String firstname, String lastname, String passport, String inn) {
  fillInput(PERSONAL_DATA_FORM_FIRSTNAME, firstname);
  fillInput(PERSONAL_DATA_FORM_LASTNAME, lastName);
  fillInput(PERSONAL_DATA_FORM_PASSPORT, passport;
  fillInput(PERSONAL_DATA_FORM_INN, inn);
}

Выглядит это примерно так, как и обычный степ. Собирать элементы в группу или в каждом методе описывать взаимодействие с каждым отдельно - на ваш вкус. Можно хоть взять пример выше и передавать вместо кучи полей 1 объект user. Принципиальной разницы тут нет.

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

Страница. Здесь мы уже берем конкретный шаблон, упаковываем нужные нам "организмы" в "тело" страницы и собираем из них тесты(как у обычного Page Object)


Итог.

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

  • создание "конструктора" форм и страниц, для максимально простого масштабирования и компоновки форм, где ограничением для нас является только бизнес логика.

  • Исключение дублирования локаторов/селекторов и их методов.

  • Предотвращение "разбухания" страниц путем разделения на слои(организм, шаблон, страница)

P.S. Не считаю что Page Object плохой и его не нужно использовать. По совокупности своих свойств, у него есть много плюсов. Цель статьи, развить "классику" в нечто большее, чтобы устранить некоторые недостатки. Это решение не претендует на звание "silver bullet" и не должно таковым быть.

P.P.S. В следующей статье более детально разберу кейсы применения.

Комментарии (0)