Можно ли обойтись без page object паттерна

В автоматизированном тестировании начал разбираться недавно.
Есть проект на Ruby, для написания тестов используется webdriver, cucumber, capybara.
Насколько критично использование page object-а?

Абсолютно не критично, это просто удобный инструмент ))).
Все зависит от подхода и предпочтений

1 лайк

Не критично, пока у вас до ~20-30 тестов. Правда, смотря каких по количеству степов и сложности.
Ну или до серьезных изменений в апликухе)

В том-то и дело, что тестов будет много, но изменения вряд ли будут глобальные, проект работает…

Есть у кого-то опыт использования аналогичных инструментво без page object-а на большом проекте?

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

  • Тю, а почему я раньше его не использовал? - произнесете вы, хватаясь за голову и жалея о потраченном времени.`

В целом, вас никто не заставляет использовать этот паттерн в чистом виде. Вы можете чем-то пренебречь. Но ключевым аспектом тут является как раз таки сама модель представления реальных вэб-страницы и их компонентов.

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

Если вы не используете модель пейджей, то возникает вопрос организации структуры вашего проекта в целом.
У вас есть тестируемый сайт. При этом, вам нужно каким-то образом описать его доменную составляющую. Зачем это нужно? Чтобы в последствии тратить минимум усилий на саппорт ваших тестов, ровно как и ускорить процесс девелопмента.

Итак, вы отказались от PageObjects. Где и как будете хранить локаторы? В один файл все спихнете? Как будете бороться с дублированием кода, если функционал реальных страниц будет технически почти идентичен? Как будете логически связывать последовательность ваших степов между собой? Статические вызовы? Как будет организована связь с webdriver’ом? Будете напрямую из методов вызывать driver.findElement... или вынесете в отдельный класс? Чем тогда по отношению к остальным классам будет являться ваш новоиспеченный с точки зрения ООП? Тут главный вопрос в том, сколько времени вы потратите на построение другого архитектурного решения, и как это будет влиять на вашу повседневную работу.

1 лайк

У меня есть опыт построения большого проект без пэдж обжектов. Проблем никаких. Проект активно развивается больше трёх лет, всё постоянно меняется и дописываются новые тесты. Никаких архитектурных блокеров мы не встречаем.

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

2 лайка

Конечно же, вы можете обойтись без пейджобжектов.

Пейджобжекты – это не сама цель, а инструмент, который делает код более “прочным” и поддерживаемым

Вместо того, чтобы убеждать о том, как важны Пейджобжекты, давайте вначале рассмотрим пример (на псевдокоде)

Черный цвет кода:

driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[5]")).Click();
driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[15]")).Click();
driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[5]")).Click();
driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[6]")).Click();
assert(driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[1]"))).Text, "6");

попро- ____

буйте ____

уга- ____
дать ____


что те- ____
сти- ____
рует ____


этот ____

код ____

пока ____
чита- ____
ете ____


этот ____
текс ____

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

Темно-серый цвет кода:

// ARRANGE
var btnThree = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[5]"));
var btnPlus = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[15]"));
var btnEq = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[6]"));
var txtResult = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[1]")));

//ACT 
btnThree.Click();
btnPlus.Click();
btnThree.Click()
btnEq.Click()

// ASSERT
assert(txtResult.Text, "6");

Поставьте лайк под этим комментарием, если вы подумали, что этот код тестирует калькулятор.

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

Чтобы не появилась мысль, что вы можете упростить задачу и подставить нужно число в индексы XPath, я скажу, что у каждого ряда кнопок свой уникальный xpath.

А кнопки “3”, “4”, “5” на самом деле в DOM модели документа идут как “5”, “3”, “4” и средствами CSS и
JavaScript позицинируется в нужном порядке при рендеринге HTML страницы.
Я это к тому, что для каждой кнопки вам нужено найти уникальный локатор.

Давайте назовем эту функцию addNumbers(operand1:int, operand2:int) : int

Серый цвет кода

function addNumbers(operand1:int, operand2:int) : int {
   var btnOne = ... ;
   var btnTwo = ... ;
   var btnThree = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[5]"));
   ...
   var btnNine = ...

   var btnPlus = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[15]"));
   var btnEq = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[2]//button[6]"));
   var txtResult = driver.findElement(By.XPath("//*[contains(@class, 'container')]/div[1]"));

   switch (operand1) {
    case 1: btnOne.Click(); 
            break;

    case 2: btnTwo.Click(); 
            break;
            ...
   }
   btnPlus.Click();
   

   // Ugly copy-paste
   switch (operand2) {
    case 1: btnOne.Click(); 
            break;

    case 2: btnTwo.Click(); 
            break;
            ....
   }

   btnEq.Click();

   return convert.toInteger(txtResult.Text);

}

Поставьте лайк если вы подумали что я наговнокодил. Если вы уже ставили лайк, то не снимайте его.

Ну да, у этого кода много недостатков. Во-первых, он поддерживает сложение только одноразрядных чисел.
Во-вторых, эти btnOne Two Three вообще нужно в нормальный человеческий массив вынести, и копипаст в switch… :poop:

Тем не менее, тест теперь выглядит вот так:

var result = addNumbers(3, 3);
assert(result, 6);

А теперь давайте подумаем, куда лучше положить эту addNumbers.

А почему бы не в класс SimpleCalculator?

var calcPage = new SimpleCalculator();
calcPage.Open();

var result = calcPage.addNumbers(3, 3);
assert(result, 6);

Таки получился PageObject… потому что, в первую очередь PageObject определяет действия над конкретной страницей.

Внутри addNumbers есть куча деклараций элементов, которые, я уверен, вы будете использовать для нового метода subtractNumbers, почему бы их не вынести и сделать полями класса SimpleCalculator?
Тогда любой метод внутри класса сможет работать с общими декларациями элементов.

Использовать или не использовать пейджобжекты – это не бинарное понятие. Ваш код будет еволюционировать и в итоге вы все равно придете к пейджобжектам. Но, это в том случае, если планируется развитие кодовой базы проекта автоматизации, добавления новых автоматизаторовю Если это проект на 30 тестов, который вы не планируете поддерживать – то SeleniumIDE – лучший выбор (там нет PageObject, а записанные тесты не жалко выбросить).

16 лайков

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

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

Без пейдж объектов ты обрекаешь себя на муки и страдания. Делай изначально правильно и все будет хорошо

1 лайк

Голословно. Мы не используем пэдж обжекты и живём прекрасно.

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

3 лайка

Почему PageObject не всегда нужны? Выскажусь и я.

Вот скажем, тестирую я приложение под windows 7. И build validation tests, как и функциональные тесты, включают в себя работу с множеством окон. Окна эти по своему содержимому - не однородны, они содержат разные контролы, они различны также и по объему логики в каждом диалоговом окне. Где-то - это проходное окошко, где нужно ввести чекбокс и указать путь. А где-то: это панель с кучей данных в таблице.

Паттерны тем и хороши, что их можно употреблять, а можно и нет. Например, такая всем известная вещь, как визард виндоус, имеет внутри себя 2-3 кнопки. Для теста важно, что я точно знаю, что оказался в нужном окне на нужном шаге и нажал 1 из 3 кнопок. Пейдж объекты хороши в вебе, где контекст можно легко воссоздавать, например, за счет просто переданного POST запроса с JSON, который должным образом тебя к работе с конкретным PageObject и вернет. Мне кажется, что это - фундаментальная важность ПейджОбъектов - то, а) работа в них заключает большой(или потенциально большой) функционал, наличие контролов, методов и проч. б) при тестировании ПейджОбъекта можно будет на него попасть, принимая преимущества REST протокола и того, что в вебе по сути можно дойти до нужного состояния Сценария не используя автоматизацию.

В ОС контекст приходится либо дольше создавать, либо не бросать его. И во втором случае как по мне PageObjects - лишнее нагромождение, затратное по времени.

А давайте сформулируем более общим образом:
Кто не видит необходимости в декомпозиции тестовой логики и интерфейса взаимодействия с исследуемым объектом?

1 лайк

Немного запутанно…
Но…

  • REST и JSON к пейджобжектам не имеют отношения
  • Представьте, что вы тестируете Single Page App, которое делалось для ентерпрайза. Так вот, роутинг (изменение URL в зависимости от состояния приложения) – это опциональная фича фреймворка на котором оно делалось, следовательно, чтобы добраться до n-й страницы, скрипту и пользователю нужно прокликать с первой.
  • В BorlandSilktest, с которого начался мой путь автоматизитора, есть понятие декларации окна как объекта в коде. Это аналог PageObject. Мы декларировали и это очень помогало не утонуть в коде. Этот подход можно использовать для всего где есть UI.
  • Да, фактически декларация PageObject будет отнимать время вначале. Чтобы сократить его я работаю над инструментом SWD Page Recorder. В случае поддержки и поиска ошибки в коде, за счет правильной архитектуры (это не только PageObject), вы можете сэкономить намного больше времени.
3 лайка

Да, но куда вынести эту функцию? Utils.java? В базовый класс?
А что если у нас есть еще логарифмический калькулятор на соседней странице и у него тоже должен быть addNumbers ?

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

Некоторые ситуации – это большие Legacy проекты, в которых можно найти код на фортране, который генерирует javascript из xslt; этот сгенерированный javascript вставляет IFRAME с SQL запросом в URL для отображения дополнительной информации. Переписать по человечески – значит потратить 3 года чтобы добится результата который есть сегодня.
Только не надо придраться, XSLT был лучшим шаблонизатором вначале 21-го века.

Из того, что вы говорите, следует, что пэдж обжекты действительно иногда полезны - когда в проекте технологии 90х, когда локаторы громоздкие, когда нет юнит-тестов, когда до разработчиков не достучаться. Вот поэтому не надо учить этому молодых, называя “хорошей практикой”.

Изначальный вопрос был - можно ли обойтись без пэдж обжектов. И мой ответ - прекрасно можно, там, где процесс разработки налажен грамотно.

[quote=“asolntsev, post:18, topic:7601”]
И мой ответ - прекрасно можно, там, где процесс разработки налажен грамотно.
[/quote]А вы писали когда-нибудь UI тесты в black_box_mode на проекте, где “процесс разработки налажен не очень грамотно”?

А видели тесты “молодых” на подобных проектах? Саппортили их?
Так может стоит перестраховаться и таки называть это “хорошей практикой”?

И расшифруйте понятие “грамотно” - это когда ты “сам на кодил сам натестил”? Топикстартер явно не из этой категории.

[quote=“asolntsev, post:18, topic:7601, full:true”]
Из того, что вы говорите, следует, что пэдж обжекты действительно иногда полезны - когда в проекте технологии 90х
[/quote]Может тогда ответите на

2 лайка

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

Вредный вопрос. Идея красивая, я согласен. Но полезнее спросить: какую проблему мы решаем? Какую реальную проблему решает эта декомпозиция? Если разобраться, ту самую проблему неэффективного процесса разработки. И не решает, а прячет.

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

4 лайка