🐛 Частые ошибки при работе с POM

← К оглавлению урока

⚡ Топ-5 ошибок POM

  • 1. Локаторы прямо в тестах, а не в классе страницы — нарушает весь смысл POM
  • 2. Инициализация элементов в __init__StaleElementReferenceException
  • 3. Нет __init__.py в pages/ModuleNotFoundError
  • 4. scope="class" + тесты меняют состояние браузера — следующий тест падает
  • 5. Нет WebDriverWait в динамических страницах — NoSuchElementException

Ошибка 1: Локаторы прямо в тестах

Симптом: при изменении UI нужно обновлять каждый тестовый файл вручную.
# Плохо: локатор в тесте
def test_login(driver):
    driver.find_element(By.ID, "user-name").send_keys("standard_user")  # локатор в тесте!
    driver.find_element(By.ID, "password").send_keys("secret_sauce")
    driver.find_element(By.ID, "login-button").click()
# Хорошо: тест вызывает методы Page Object
def test_login(driver):
    login_page = LoginPage(driver)
    login_page.success_login("standard_user", "secret_sauce")

Ошибка 2: Инициализация элементов в __init__

Симптом: StaleElementReferenceException при использовании поля, если страница обновилась.
# Плохо: элемент ищется при создании объекта
class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        self.username_input = driver.find_element(By.ID, "user-name")  # ОШИБКА

    def enter_username(self, text):
        self.username_input.send_keys(text)  # упадёт, если DOM обновился
# Хорошо: элемент ищется при вызове метода
class LoginPage(BasePage):
    USERNAME_INPUT = (By.ID, "user-name")

    def enter_username(self, text):
        self.type_text(self.USERNAME_INPUT, text)  # поиск происходит здесь

Ошибка 3: Отсутствие __init__.py

Симптом: ModuleNotFoundError: No module named 'pages'
# Структура без __init__.py — ОШИБКА
pages/
    login_page.py         # нет __init__.py

# В тесте:
from pages.login_page import LoginPage  # ModuleNotFoundError!
# Правильно: создать пустые __init__.py
pages/
    __init__.py           # пустой файл — делает pages/ пакетом
    login_page.py

# Теперь импорт работает:
from pages.login_page import LoginPage  # OK

Ошибка 4: scope="class" + грязное состояние

Симптом: второй тест падает из-за состояния, которое оставил первый тест.
# Плохо: test_add_to_cart не возвращает браузер в исходное состояние
class TestInventory(BaseTest):
    def test_add_to_cart(self):
        self.login_page.success_login("standard_user", "secret_sauce")
        self.inventory_page.add_item_to_cart("Sauce Labs Backpack")
        # Корзина теперь не пуста! Браузер остался на inventory.html

    def test_cart_count_zero(self):
        # ПАДАЕТ: корзина не пуста, берём счётчик — там 1
        assert self.inventory_page.get_cart_count() == 0
# Хорошо: каждый тест начинает с известного состояния
class TestInventory(BaseTest):
    def test_add_to_cart(self):
        self.login_page.open()
        self.login_page.success_login("standard_user", "secret_sauce")
        self.inventory_page.add_item_to_cart("Sauce Labs Backpack")

    def test_cart_count_zero(self):
        # Явно открываем нужную страницу — не зависим от предыдущего теста
        self.login_page.open()
        self.login_page.success_login("standard_user", "secret_sauce")
        assert self.inventory_page.get_cart_count() == 0

Ошибка 5: find_element вместо WebDriverWait

Симптом: NoSuchElementException на динамических страницах (AJAX, SPA).
# Плохо: элемент ищется мгновенно, до загрузки
class InventoryPage:
    def get_items(self):
        return self.driver.find_elements(By.CLASS_NAME, "inventory_item")  # могут не загрузиться
# Хорошо: явное ожидание через BasePage.find_all
class InventoryPage(BasePage):
    INVENTORY_ITEMS = (By.CLASS_NAME, "inventory_item")

    def get_items(self):
        return self.find_all(self.INVENTORY_ITEMS)  # ждёт appearance

Ошибка 6: Дублирование WebDriverWait в каждом методе

Симптом: код разбухает, тайм-аут указывается в разных местах по-разному.
# Плохо: WebDriverWait создаётся заново в каждом методе
class LoginPage:
    def get_username_input(self):
        return WebDriverWait(self.driver, 10).until(...)  # дубль!

    def get_password_input(self):
        return WebDriverWait(self.driver, 10).until(...)  # дубль!
# Хорошо: WebDriverWait создаётся один раз в BasePage
class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)  # один раз

    def find(self, locator):
        return self.wait.until(EC.presence_of_element_located(locator))

Ошибка 7: Сложная логика в методах тестов

Симптом: методы тестов содержат XPath и find_element — при изменении UI ломается везде.
# Плохо: бизнес-логика в тесте
def test_backpack_price(self):
    price = self.driver.find_element(
        By.XPATH,
        "//div[text()='Sauce Labs Backpack']/..//div[@class='inventory_item_price']"
    ).text  # XPath в тесте — нарушение POM
# Хорошо: логика поиска цены в InventoryPage
# pages/inventory_page.py
def get_item_price(self, item_name):
    xpath = f"//div[text()='{item_name}']/ancestor::div[@class='inventory_item']//div[@class='inventory_item_price']"
    return self.find((By.XPATH, xpath)).text

# tests/test_inventory.py
def test_backpack_price(self):
    price = self.inventory_page.get_item_price("Sauce Labs Backpack")  # чисто