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

При наличии класса с локаторами, где хранить логику работы с ними

1.0 Junior🔥 102 комментариев
#Soft skills и карьера

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

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

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

Где хранить логику работы с локаторами в тестовом фреймворке

Ключевой вопрос при построении устойчивого и поддерживаемого тестового фреймворка. При наличии отдельного класса (или модуля) с локаторами, хранение логики работы с ними требует продуманного архитектурного подхода. Основная цель — соблюсти принципы DRY (Don't Repeat Yourself), инкапсуляции и низкой связанности.

Основные архитектурные подходы и паттерны

1. Page Object Model (POM) — классический и наиболее распространенный

Логика работы с локаторами инкапсулируется внутри Page Object классов. Класс локаторов выступает как поставщик селекторов, а Page Object использует их, добавляя поведение.

# Класс с локаторами (селекторами)
class LoginPageLocators:
    USERNAME_INPUT = "#username"
    PASSWORD_INPUT = "#password"
    SUBMIT_BUTTON = "button[type='submit']"
    ERROR_MESSAGE = ".alert-error"

# Page Object, который хранит логику работы с этими локаторами
class LoginPage:
    def __init__(self, driver):
        self.driver = driver
    
    def enter_username(self, username):
        element = self.driver.find_element(By.CSS_SELECTOR, LoginPageLocators.USERNAME_INPUT)
        element.clear()
        element.send_keys(username)
    
    def login(self, username, password):
        self.enter_username(username)
        self.driver.find_element(By.CSS_SELECTOR, LoginPageLocators.PASSWORD_INPUT).send_keys(password)
        self.driver.find_element(By.CSS_SELECTOR, LoginPageLocators.SUBMIT_BUTTON).click()
    
    def get_error_message(self):
        return self.driver.find_element(By.CSS_SELECTOR, LoginPageLocators.ERROR_MESSAGE).text

Преимущества:

  • Полная инкапсуляция: тест знает только о методах LoginPage.
  • Упрощение поддержки: изменения в верстке вносятся в 2 местах (локаторы + возможно, логику), а не во всех тестах.
  • Читаемость тестов: тесты выглядят как бизнес-сценарии.

2. Page Element (или Component) Pattern — для сложных UI-компонентов

Когда элементы интерфейса повторяются (таблицы, выпадающие списки, модальные окна), логику работы с ними выносят в отдельные классы компонентов.

# Класс с локаторами для таблицы
class UserTableLocators:
    TABLE = "table.users"
    HEADER = "thead th"
    ROWS = "tbody tr"
    CELL = "td"

# Класс, инкапсулирующий логику работы с таблицей
class UserTableComponent:
    def __init__(self, driver, container_locator=UserTableLocators.TABLE):
        self.driver = driver
        self.container = driver.find_element(By.CSS_SELECTOR, container_locator)
    
    def get_row_count(self):
        return len(self.container.find_elements(By.CSS_SELECTOR, UserTableLocators.ROWS))
    
    def get_cell_text(self, row_index, column_index):
        rows = self.container.find_elements(By.CSS_SELECTOR, UserTableLocators.ROWS)
        cells = rows[row_index].find_elements(By.CSS_SELECTOR, UserTableLocators.CELL)
        return cells[column_index].text

# Использование в Page Object
class AdminPage:
    def __init__(self, driver):
        self.driver = driver
        self.user_table = UserTableComponent(driver)
    
    def get_first_user_name(self):
        return self.user_table.get_cell_text(0, 1)

3. Использование Миксинов (Mixins) — для переиспользуемой логики

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

# Базовый класс с общей логикой
class BasePage:
    def __init__(self, driver):
        self.driver = driver
    
    def _type(self, locator, text):
        element = self.wait.until(EC.element_to_be_clickable(locator))
        element.clear()
        element.send_keys(text)
    
    def _click(self, locator):
        self.wait.until(EC.element_to_be_clickable(locator)).click()

# Page Object наследует логику и использует локаторы
class LoginPage(BasePage, LoginPageLocators):
    def __init__(self, driver):
        super().__init__(driver)
        self.wait = WebDriverWait(driver, 10)
    
    def login(self, username, password):
        self._type((By.CSS_SELECTOR, self.USERNAME_INPUT), username)
        self._type((By.CSS_SELECTOR, self.PASSWORD_INPUT), password)
        self._click((By.CSS_SELECTOR, self.SUBMIT_BUTTON))

4. Полное отделение: Локаторы → Страницы → Шаги

В BDD-подходе или многослойном фреймворке может использоваться трехуровневая структура:

  • Уровень локаторов: чистые константы.
  • Уровень Page Object: методы работы с элементами.
  • Уровень шагов (Step Definitions или бизнес-логика): композиция методов Page Object в бизнес-действия.

Рекомендации и лучшие практики

  • Единая ответственность: Класс с локаторами должен только хранить локаторы, без логики. Логика — ответственность Page Object или компонентов.
  • Именование локаторов: Имена должны отражать бизнес-сущность, а не детали реализации (LOGIN_BUTTON, а не BLUE_BUTTON).
  • Ленивая инициализация элементов: Вместо поиска всех элементов в __init__, используйте property или методы для поиска по требованию, чтобы избежать StaleElementReferenceException.
    @property
    def username_input(self):
        return self.driver.find_element(By.CSS_SELECTOR, self.USERNAME_INPUT)
    
  • Ожидания (Waits): Интегрируйте явные ожидания внутрь методов Page Object, это сделает тесты стабильнее.
  • Отчетность и логирование: Добавляйте в методы логирование действий, что упрощает отладку падающих тестов.

Заключение

Логику работы с локаторами следует хранить в Page Object классах и, при необходимости, в отдельных классах компонентов. Это обеспечивает:

  • Сопровождаемость: Изменения в UI затрагивают минимальное количество мест.
  • Читаемость: Тесты описывают бизнес-сценарии, а не технические детали.
  • Переиспользуемость: Общая логика выносится в базовые классы или компоненты.
  • Надежность: Централизованное управление ожиданиями и обработкой ошибок.

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

При наличии класса с локаторами, где хранить логику работы с ними | PrepBro