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

Код покрыт юнит-тестами, с разработачиками сотрудничаю)
Если говорить откровенно то у меня слабые знания в языке программирования, я конечно его активно учу, но не все как говорится сразу.
Выбрал cucumber потому что gherkin позволяет составлять человеческо понятные тесты.
Далее, Capybara добавляет того сахара, который упрощает всё.
Как локаторы использую xpath, некоторые да, повторяются, но я делаю дополнительные проверки.

Я понимаю, что это не совсем верно, не зная толком мат.части браться писать что-то. Но в данных условиях по-другому просто не получается.
Пробовал site-prism, непонравился. посчитал излишним нагромождением, DRY обхожу благодаря более обстоятельному описанию шагов, которое как мне кажется может помочь в будущем сразу понять для чего данный тест предназначен.

Имеет ли шансы мой подход, или же нет?
Только давайте объективно)

Можете писать во flat стиле, оставаясь при этом DRY? Извините - не верю.

1 лайк

Я свой фреймворк переделывал 3 раза, пока не прочитал, что существуют паттерны и в сфере автоматизации используется PageObject. Может стоило в этой теме задаться вопросом, а понимает ли человек что такое PageObject? P.S. я не хочу никого оскорбить, но это действительно так и выглядит, не знание что такое паттерны.

1 лайк

Простите, шапочно знаком с джавой и не понимаю, что вы имеете в виду.

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

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

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

4 лайка

Я писал автотесты на указанном стеке. Встречный вопрос - а почему не использовать? Задача - мы хотим инкапсулировать логику нахождения html элементов на странице. PageObject - не единственный возможный паттерн, но самый очевидный. На ruby есть готовые реализации, так что самому надо будет описывать только локаторы. Итак, в чем причина сомнений?

Но вообще да, встречался с ситуацией, в которой PageObject был бы неудачным решением. Проблема была с переиспользованием html объектов с динамическими локаторами, имеющиеся реализации PageObject на Java этого не позволяли.

1 лайк

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

2 лайка

Ну это, мягко говоря, спорное утверждение. Есть ссылка на источник, как должно быть “на самом деле”? Давайте тогда начнем определения, что это вообще такое - “PageObject pattern” :smile:

PageObject’ы используют

  • и чтобы прятать локаторы: давать им красивые имена и нормальную поддержу рефакторинга
  • и чтобы инкапсулировать логику работы с этими элементами
  • а также чтобы упростить совместную работы команды

Вариантов как использовать PageObject’ы – множество. Нужно выбрать тот, который работает лучше всего для конкретного ЯП и проекта.

Причиной не использовать PageObject’ы может быть, как уже ополоснулось, проект на 10-30 тестов, который нужно за день накодить и забыть. Другая причина – это незнание основ ООП, которые в случае PageObject очень важны.

@asolntsev, вот вы, вроде бы, какие-то веб-приложения на Java разрабатываете. Неужели вы его тоже пишете в flat -стиле?:

10: if (User_Accaunt_isLoggedIn(httpContext, ACL, userName)) {
         int userFlagHandle = Project_Project_Settings_setLoginFlag(httpContext, ACL, User_Accaunt_getUserId(httpContext, userName), true);
       if (SQLBackend_getCurrentBackendName() == "Oracle")  {
          Oracle_SQL("INSERT INTO User VALUES" + (String)Project_Project_Settings_getLoginFlag(httpContext, ACL, User_Accaunt_getUserId(httpContext, userName));)
       }
      GOTO 20;
}
20: 

Конечно, можно сказать что это очень понятный flat-стиль код в контексте 10-ти строк… Но в масштабах большого проекта (приложения либо проект по автоматизации) – не использование классов и объектов (а PageObject не далеко стоит от обычного класса) мне кажется очень сомнительным, хотя и возможным.

2 лайка

Мне кажется тут все крутится вокруг идеологии того, что UI тестов должно быть мало.

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

Сейчас работаю над проектом, в котором пишутся и unit, и integration tests для бэкэенда, но UI полностью отделен и живет своей жизнью (JS frameworks) + unt тестов там нет. Помимо всего прочего, используются сервисы, написанные ранее другими командами. Ввиду масштабности и сложности продукта, есть множество сценариев, которые чисто технически очень сложно или вовсе невозможно покрыть на компонентном и интеграционном уровнях. К примеру, smart-cards, MFA, кастомные браузерные плагины / расширения, отдельные десктопные части, взаимодействующие с Web internally и т.п. Так вот в такой ситуации UI тесты - это связующее звено между frontend и backend. Потенциальные проблемы могут быть везде, а основные payment workflows зависят от внешних компонентов, к которым девелоперы никак не смогут получить доступ. И что они будут в итоге покрывать юнит тестами? Весь критический для бизнеса флоу останется непокрытым.

Итого, кол-во тестов у нас немалое + незабываем о кроссбраузерности (достаточно болезненный момент ввиду наличия независимого JS фронтэнда, в котором постоянно что-то меняется).

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

Тут прозвучало много различных определений и назначений page objects. Но давайте рассмотрим вопрос на более высоком уровне. Мы тестируем продукт определенной отрасли. Мы каждый день внутри команды общаемся посредством DSL (domain specific language). Мы создаем backend / UI код, называя методы / компоненты на языке домена… А UI тесты мы пишем на каком уровне? Не unit, не integration, а system (sys integration) / acceptance. Т.е. взаимодействуем с системой мы как? На уровне внешней вэб-оболочки. Внешняя оболочка представлена в виде набора из N страниц, логически объединенных между собой, и отражающих суть нашего бизнеса. Т.е. любая бизнес-операция может быть представлена в виде цепочки действий. Причем, каждое звено этой цепи логически ограничивает нас в путях возможного развития сценария. К примеру, мы не можем отправить платеж, без подтверждения заинтересованными лицами, т.е. функционал сайта не позволит нам осуществить send, минуя approve.

PageObject паттерн прежде всего призывает нас соблюдать бизнес модель нашего приложения: DSL именование, логическое компонентное разделение, строгое ограничение по контексту (невозможность использования логически неверных шагов) и т.п. Тем самым, мы защищаем себя от логических ошибок, посредством тестов общаемся с приложением на языке самого приложения, минимизируем объем рефакторинга в случае изменения бизнес логики / layout’а, соблюдаем UI структуру компонентов, снижаем порог вхождения для новых членов команды и т.п.

В общем, это совсем не призыв бездумного использования сего паттерна всегда и везде. Просто мне кажется, что на данный момент больший процент автоматизации задействован именно в аутсорсе, где идеальных процессов по факту почти нет. Плюс, не стоит забывать о множестве дополнительных факторов, которые так или иначе усложняют нам жизнь. Выбор архитектуры / инструментов / технологий / паттернов - достаточно сложный и кропотливый процесс, который, как говорится, depends on… Используйте то, что действительно обосновано в вашем конкретном случае, но при этом, не забывайте о сути и назначении паттернов. :wink:

7 лайков

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

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

И нет, наш процесс вовсе не идеален. Просто это тупо экономически выгоднее (сотрудничать и писать юнит-тесты), чем тратить кучу усилий на параллелизацию, пэдж обжекты и прочую инфраструктуру. И пожалуйста, не надо говорить, что “у нас проект сложный”. :smile: У всех сложный. Это самое популярное оправдание, чтобы ничего не менять.

Пожалуйста, вот статья Мартина Фаулера: PageObject

The page object should encapsulate the mechanics required to find and manipulate the data in the gui control itself.
Прятать логику для поиска и манипуляции с элементами!

A good rule of thumb is to imagine changing the concrete control - in which case the page object interface shouldn’t change.

Золотые слова! Когда элемент меняется (скажем, с RADIO на SELECT), интерфейс пэдж обжекта не должен меняться.

@asolntsev Сколько у вас тестов на проекте?

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

PageObject - это шаблон для абстрагирования тестов от реализации интерфейса страницы. Он предоставляет нижележащему уровню (тестам, шагам) API для взаимодействия со страницей, скрывая особенности реализации. Таким образом интерфейс страницы может меняться не ломая работу тестов.

Абстрагировать можно и на других уровнях. Потому что страницы тоже не вечны и могут не только менять реализацию внутри API, но и сами со временем исчезать, сливаясь с другими страницами, при переработке интерфейса. Поэтому я, например, стараюсь выделять ещё такой уровень абстракции как бизнес-функционал (это типа DSL, но не обязательно на человеческом языке), который по своей сути практически не меняется. И именно его использую в тестах, таким образом сами тесты практически не меняются.

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

Андрей, ну вот и как ты предлагаешь решать такую проблему без Page Object? Работать в тесном контакте с разработчиками позволит только не получать такие проблемы неожиданно - ты сможешь менять тесты вместе с изменением интерфейса. Но менять тебе придется КАЖДЫЙ тест, которого коснется это изменение

@uslashka Примерно 3000 юнит-тестов и 300 UI-тестов.

@joemast Поддерживаю! Я об этом и говорю: пэдж обжекты надо использовать не как помойку локаторов, а для DSL из бизнес-операций.

@joemast Прежде всего, мы используем библиотеку Selenide, а она берёт на себя большУю часть этих проблем. Например, метод $("#fname").val("john") или $("#fname").shouldHave(value("john")) работает одинаково хорошо и с INPUT, и с SELECT.

@joemast Так я о том и твержу раз за разом! Этих тестов должно быть мало! Сотрудничество с разработчиками означает, что они сами пишут юнит-тесты и покрывают все возможные комбинации юнит-тестами. И поэтому UI-тестами не надо покрывать много сценариев. Не надо не потому, что лень, а потому, что это вредно! UI-тестов должно быть мало. Они должен покрывать только самые базовые сценарии.

Моё золотое правило:

Тестов должно быть как можно больше,
но
UI-тестов должно быть как можно меньше.

Мы реально так и работаем. В тех (очень редких) ситуациях, когда INPUT меняется на SELECT (и Selenide не поддерживает их одинаково), я меняю INPUT на SELECT в одном-двух UI-тестах. Это просто и быстро.

Если кто ещё не видел, вот мой доклад на Codefest 2015 про экономически эффективный процесс тестирования: Экономически эффективный процесс тестирования

2 лайка

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

@asolntsev, я вас совсем не могу понять. У меня просто происходит разрыв шаблона: плавный, но очень целеустремленный.

Вы же начали спорить о том, что тесты в flat-стиле это вообще никакой не переходной этап перед использованием PageObject… Сейчас же вы яростно защищаете PageObject как средство создание DSL в проекте. Нет, ну сейчас то вы на нашей стороне, но почему-то продолжаете войну.

Selenide… не смотря на то, что Selenide поддерживает PageObject (секция Classic PageObject)
http://selenide.org/documentation/page-objects.html
В самой документации и тут на форуме, вы активно продвигаете анти-паттерн магических строк.

public class GoogleSearchPage {
  @FindBy(how = How.NAME, using = "q")
  private SelenideElement searchBox;

  public GoogleResultsPage search(String query) {
    searchBox.setValue(query).pressEnter();
    return page(GoogleResultsPage.class);
  }
}

В самом примере, используется обвертка SelenideElement… но, я уверен что и обычный PageObject из коробки будет работать с Selenide ( $ – поддерживает WebElement как параметр):

public class GoogleSearchPage {
  @FindBy(how = How.NAME, using = "q")
  private WebElement searchBox;

  public GoogleResultsPage search(String query) {
    $(searchBox).setValue(query).pressEnter(); // <--- 
    return page(GoogleResultsPage.class);
  }
}

Вопрос в том: почему же вы так негативно отзываетесь о Пейджобжектах в самой документации:

This style has some disadvantages, but if you want, Selenide allows it

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

public class GoogleSearchPage {
  public GoogleResultsPage search(String query) {
    $(By.name("q")).setValue(query).pressEnter();
    return page(GoogleResultsPage.class);
  }
}

Вы действительно считаете что вот это:

$(By.name("q")).setValue(query).pressEnter();

намного читабельнее вот этого?

$(searchBox).setValue(query).pressEnter();

Возвращаясь к цитате:

$("#fname").val("john")

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

Читая код, вот вопрос на миллион долларов:

Что означает #fname?

9 лайков