Разные подходы к Page object паттерну и где лучше хранить локаторы

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

Для всех локаторов создаётся отдельный класс, все findElement должны находиться тоже в классе локаторов. Класс самой страницы наследует класс локаторов и содержит только клики и прочие методы. Весь поиск элементов вынесен в отдельный класс.

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

Есть ли тут люди, использующие такой разделённый подход? Поделитесь опытом, чем это может быть лучше и полезнее? Спасибо :slight_smile:

1 лайк

Лучше опишите ваш подход в формате code snippets. Дабы было видно хотя бы абстрактный пример реализации классов с локаторами, поиском и пейджами.

1 лайк

Мы к такому подходу вынужденно пришли.
Дело в том что есть элементы довольно сложные и мы сделали для них кастомные типы. В итоге описательная часть занимает довольно значительный объём, а ещё и нужно описать методы для работы с этими элементами.
Как пример один из классов описания элементов занимает 460 строк и ещё 420 строк непосредственно методов работы с этими элементами. Если это слить в один класс будет неудобно работать.
Разнесли по разным package каждую страницу. В package лежит класс описания элементов страницы, который package accesible и класс методов, который public.
Главное сделать единообразно во всём проекте.

1 лайк

@Sergewar , я думаю вы неверно поняли автора. @ellina_kulagina имеет ввиду, что все локаторы вынесены в отдельный класс, а вы говорите что выделили какой-то элемент как отдельную сущность и вынесли реализацию в другой класс.
Я думаю, что выносить локаторы не верно, потому что они относятся к странице. Если у вас их очень много и это была причина, для того чтобы вынести их - я бы посоветовал разбить страницу на логические части и потом их агрегировать вместе в этой странице, вот так будет более удобочитаемо и поддерживаемая структура.

2 лайка

Я прекрасно понял @ellina_kulagina. У нас, в текущем проекте, вынесены локаторы в отдельный класс. И согласен что выносить их отдельно не верно. Поэтому и приняли решение ограничить область видимости класса с локаторами только package доступным, а класс с методами уже public.

это решение что ли?

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

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

Так новые тесты заставляют так писать, продвигая такой подход как единственно верный.

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

Page Object Pattern - это же самый базовый подход.
Если тестер не умеет / не понимает его, то это даже не Junior…

Я не совсем понял - у вас вопрос про то зачем нужен Page Object Pattern? Или зачем нужен именно такой способ делать Page Object Pattern, как вы его описываете?

Главная идея метода - энкапсуляция деталей страницы в отдельные классы от тестов. Кто от кого наследуется, сколько там классов - это уже второй вопрос.

То есть поиск элементов и работа с ними - это PageObjects.
А логика тестов - отдельно.
Очень логично.

Вот пример того зачем это надо:
Без Page Object есть вот такой код тест кэйса:

@Test
void verifyRating () {
  var Albums = findElementsWithClass('albums');
  var Record = Albums.findElementsWithId('Album1');
  Record.Click();
  var Rating = Record.findElementWithClass('ratings-fields');
  Assert Rating==5, 'album rating is incorrect';
}

А вот так с Page Objects:

@Test
void verifyRating () {
  var Rating = Albums.getRating('Album1');
  Assert Rating==5, 'album rating is incorrect';
}

А если представить что похожих тест кэйсов несколько, то копи-пастить findElementByClass - это вообще неправильно. В случае же с Page Object все классы и Ids - в одном месте.

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

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

Вы меня не поняли. Вот есть класс TestPage.

 public class TestPage
{
    [FindsBy(How = How.Id, Using = "test")]
    public IWebElement TestButton;

  public void ClickTestButton()
    {
        TestButton.Click();
    }   
}

Это мой подход, когда все FindsBy в начале класса, а дальше методы работы с элементами. Если локтор сломается, то вот он тут, в одном единственном месте и проблем нету с его поиском.

Подход, который мне предлагают использовать.

public class TestLocators
{
    [FindsBy(How = How.Id, Using = "test")]
    public IWebElement TestButton;
}

public class TestPage : TestLocators
{
    public void ClickTestButton()
    {
        TestButton.Click();
    }   
}

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

1 лайк

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

Лично мне нравится подход где локаторы страницы описаны в отдельном классе. Да это заставляет создавать еще один класс, но когда тестов и локаторов больше 1000 то выглядит это как бы поудобнее. По сколько я использую селенид то и никаких @FindBy у меня нет, следовательно, код с локаторами еще меньше и еще лучше читается.
В обще это все на любителя, лучше используйте то что вам нравится и то с чем вам удобно работать, если удобно держать все в одном классе то делайте так и не слушайте других.

1 лайк

У вас на одной странице 1000 локаторов?

ааааааааааааааааааааааааааа.

По теме - Эллина, практического смысла в таком наследовании нет, можете предложить partial классы, если ребятам прям хочется все держать в отдельном файле.

1 лайк

ахинею пишете, вы совсем не поняли проблему автора

какие такие поиски? Если вы в любом подходе тратите время на поиски, то у вас уже архитектура неправильная.