← Назад к вопросам

Как бы разбил стартовую страницу Яндекс на паттерн Page Object?

2.3 Middle🔥 182 комментариев
#Selenium и UI автоматизация#Архитектура приложений

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Стратегия разбиения стартовой страницы Яндекса на паттерн Page Object

Разбиение сложной и динамичной страницы, такой как стартовя страница Яндекса (yandex.ru), на Page Object Model (POM) требует тщательного анализа структуры и функциональных блоков. Моя стратегия основана на принципах модульности, повторного использования и четкого разделения ответственности. Я рассматриваю страницу как совокупность независимых, но взаимодействующих компонентов.

Анализ ключевых элементов страницы

Стартовя страница Яндекса содержит несколько явных функциональных блоков:

  1. Поисковая форма с полем ввода, кнопкой и дополнительными элементами (например, "Все", "Картинки").
  2. Навигационная панель (сервисы: Яндекс, Почта, Диск, Маркет и т.д.).
  3. Блок новостей и информационного потока (главные новости, персонализированные рекомендации).
  4. Контекстные виджеты (погода, курс валют, афиша — их наличие и состав могут меняться).
  5. Интерактивные элементы (кнопка меню, переключатель тем, региональные настройки).

Архитектура классов Page Object

Я бы создал иерархию классов, отражающую эту структуру:

1. Базовый класс YandexMainPage Это главный Page Object, который представляет собой всю страницу. Он содержит ссылки на все компоненты и методы для высокоуровневых действий (например, переход в почту, выполнение поиска). Его конструктор инициализирует все внутренние компоненты.

public class YandexMainPage {
    private SearchForm searchForm;
    private NavigationBar navigationBar;
    private NewsFeed newsFeed;
    private WidgetPanel widgetPanel;

    public YandexMainPage(WebDriver driver) {
        PageFactory.initElements(driver, this);
        this.searchForm = new SearchForm(driver);
        this.navigationBar = new NavigationBar(driver);
        // ... инициализация других компонентов
    }

    public SearchResultsPage searchFor(String query) {
        return searchForm.performSearch(query);
    }

    public void navigateToService(String serviceName) {
        navigationBar.selectService(serviceName);
    }
    // ... другие высокоуровневые методы
}

2. Класс компонента SearchForm Инкапсулирует всю логику работы с поисковой формой. Это идеальный пример для POM: локаторы и действия сосредоточены в одном месте.

class SearchForm:
    def __init__(self, driver):
        self.driver = driver
        self.search_input = (By.ID, "text")
        self.search_button = (By.CSS_SELECTOR, "button[type='submit']")
        self.suggest_list = (By.CLASS_NAME, "mini-suggest__item")

    def enter_query(self, query_text):
        self.driver.find_element(*self.search_input).send_keys(query_text)
        return self  # Возвращает сам объект для цепочек действий

    def click_search_button(self):
        self.driver.find_element(*self.search_button).click()
        return SearchResultsPage(self.driver)  # Возвращает следующую страницу

    def get_suggestions(self):
        # Метод для работы с выпадающими подсказками
        return [elem.text for elem in self.driver.find_elements(*self.suggest_list)]

3. Класс компонента NavigationBar Представляет панель сервисов. Его методы возвращают либо новые Page Objects (например, YandexMailPage), либо выполняют переходы.

4. Класс компонента NewsFeed и WidgetPanel Для более сложных или динамичных блоков я мог бы использовать Page Element или Widget подход. Например, WidgetPanel мог бы предоставлять методы для проверки наличия определенного виджета или получения его данных.

public class WidgetPanel {
    private WebDriver driver;
    @FindBy(css = "[data-testid='weather-widget']")
    private WebElement weatherWidget;

    public boolean isWeatherWidgetPresent() {
        return weatherWidget.isDisplayed();
    }

    public String getCurrentTemperature() {
        return weatherWidget.findElement(By.cssSelector(".temp")).getText();
    }
}

Ключевые принципы, примененные в разбиении

  • Разделение ответственности: Каждый класс отвечает только за свой логический блок интерфейса. Это упрощает поддержку: изменения в навигационной панели затрагивают только класс NavigationBar.
  • Инкапсуляция локаторов: Все селекторы (By.ID, CSS пути) скрыты внутри классов компонентов. Тесты работают только через публичные методы (performSearch(), selectService()), что делает их устойчивыми к мелким изменениям в HTML-структуре.
  • Возвращение новых Page Objects: Методы, которые приводят к переходу на другую страницу (например, поиск), возвращают экземпляр Page Object этой новой страницы (SearchResultsPage). Это обеспечивает естественную цепочку действий в тестах.
  • Управление сложностью: Вместо одного монолитного класса с сотнями методов и локаторов, мы получаем набор небольших, понятных классов. Это особенно важно для такой насыщенной страницы, как Яндекс.
  • Повторное использование: Компоненты, такие как SearchForm, могут потенциально быть использованы на других страницах Яндекса (например, на странице видео), что позволяет создать библиотеку общих UI-компонентов.

Пример теста, использующий эту структуру

@Test
public void testSearchAndNavigation() {
    YandexMainPage mainPage = new YandexMainPage(driver);
    // Использование компонента поиска через главную страницу
    SearchResultsPage resultsPage = mainPage.searchFor("автоматизация тестирования");
    resultsPage.assertResultsContain("Selenium");

    // Использование компонента навигации
    mainPage.navigateToService("Почта");
    // Предполагается, что метод возвращает YandexMailPage
    // Дальнейшие проверки почтового интерфейса...
}

Таким образом, разбиение на Page Object превращает стартовую страницу Яндекса из монолитного объекта в набор управляемых, слабо связанных компонентов. Это значительно повышает читаемость, поддерживаемость и масштабируемость тестового кода, что является основной целью применения данного паттерна в автоматизации UI-тестирования.