⚖️ Старый vs Новый: POM-рефакторинг
Здесь показано, как выглядели примеры из лекции (старый стиль) и как они трансформируются в современный POM с BasePage.
⚡ Главные переходы
- Локаторы в тестах → локаторы в классе страницы
driver.find_element(By.ID, "...")→page.method()- LoginPage без BasePage (из лекции) → LoginPage(BasePage) с общими методами
- Фикстуры в каждом классе → BaseTest с
autouse=True
Переход 1: тесты с голыми локаторами → LoginPage
Из лекции (старое): тест напрямую ищет элементы через
driver.find_element.
# tests/test_login.py — ИЗ ЛЕКЦИИ (старый подход)
def test_success_login_valid_data(driver):
user_name_input_field = driver.find_element(By.ID, "user-name")
user_name_input_field.send_keys("standard_user")
password_input_field = driver.find_element(By.ID, "password")
password_input_field.send_keys("secret_sauce")
login_button = driver.find_element(By.ID, "login-button")
login_button.click()
assert driver.current_url == "https://www.saucedemo.com/inventory.html"
def test_invalid_password(driver):
user_name_input_field = driver.find_element(By.ID, "user-name") # дубль
user_name_input_field.send_keys("standard_user")
password_input_field = driver.find_element(By.ID, "password") # дубль
password_input_field.send_keys("wrong_password")
login_button = driver.find_element(By.ID, "login-button") # дубль
login_button.click()
error_message = driver.find_element(By.CLASS_NAME, "error-message-container")
assert "Username and password do not match" in error_message.text
Современно (POM): тест работает с методами страницы, не знает локаторов.
# tests/test_login.py — СОВРЕМЕННЫЙ СТИЛЬ (POM)
from tests.base_test import BaseTest
class TestLogin(BaseTest):
def test_valid_login(self):
self.login_page.success_login("standard_user", "secret_sauce")
assert "inventory.html" in self.driver.current_url
def test_invalid_password(self):
self.login_page.open()
self.login_page.success_login("standard_user", "wrong_password")
error = self.login_page.get_error_message()
assert "Username and password do not match" in error
Переход 2: LoginPage без BasePage → LoginPage(BasePage)
Из лекции (старое):
LoginPage сам создаёт WebDriverWait и напрямую ищет элементы через wait.until(...).
# pages/login_page.py — ИЗ ЛЕКЦИИ (старое)
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def get_username_input(self):
return self.wait.until(EC.presence_of_element_located((By.ID, "user-name")))
def get_password_input(self):
return self.wait.until(EC.presence_of_element_located((By.ID, "password")))
def get_login_button(self):
return self.wait.until(EC.element_to_be_clickable((By.ID, "login-button")))
def enter_username(self, username):
field = self.get_username_input()
field.clear()
field.send_keys(username)
def enter_password(self, password):
field = self.get_password_input()
field.clear()
field.send_keys(password)
def click_on_login_button(self):
self.get_login_button().click()
Современно (POM с BasePage):
LoginPage наследует общую логику ожидания из BasePage. Локаторы — атрибуты класса.
# pages/login_page.py — СОВРЕМЕННЫЙ СТИЛЬ (с BasePage)
from selenium.webdriver.common.by import By
from pages.base_page import BasePage
class LoginPage(BasePage):
# Локаторы как атрибуты класса — видны сразу при чтении
USERNAME_INPUT = (By.ID, "user-name")
PASSWORD_INPUT = (By.ID, "password")
LOGIN_BUTTON = (By.ID, "login-button")
ERROR_MESSAGE = (By.CLASS_NAME, "error-message-container")
URL = "https://www.saucedemo.com/"
def open(self):
self.driver.get(self.URL)
def enter_username(self, username):
self.type_text(self.USERNAME_INPUT, username) # из BasePage
def enter_password(self, password):
self.type_text(self.PASSWORD_INPUT, password) # из BasePage
def click_login(self):
self.click(self.LOGIN_BUTTON) # из BasePage
def success_login(self, username, password):
self.enter_username(username)
self.enter_password(password)
self.click_login()
def get_error_message(self):
return self.get_text(self.ERROR_MESSAGE) # из BasePage
Переход 3: фикстуры в каждом классе → BaseTest
Из лекции (промежуточный): инициализация driver и страниц повторяется в каждом тестовом классе.
# tests/test_inventory.py — ИЗ ЛЕКЦИИ (промежуточный)
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from pages.inventory_page import InventoryPage
from pages.login_page import LoginPage
class TestInventory:
@pytest.fixture(scope="class")
def driver(self):
d = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
d.maximize_window()
d.get("https://www.saucedemo.com/")
yield d
d.quit()
@pytest.fixture(scope="class")
def inventory_page(self, driver):
return InventoryPage(driver)
@pytest.fixture(scope="class")
def login_page(self, driver):
return LoginPage(driver)
def test_items_amount(self, inventory_page, login_page):
login_page.success_login("standard_user", "secret_sauce")
assert inventory_page.get_items_amount() == 6
Современно (BaseTest): инициализация один раз в
BaseTest, все классы наследуются от него.
# tests/test_inventory.py — СОВРЕМЕННЫЙ СТИЛЬ (BaseTest)
from tests.base_test import BaseTest
class TestInventory(BaseTest):
# driver, login_page, inventory_page — уже доступны через BaseTest
def test_items_amount(self):
self.login_page.success_login("standard_user", "secret_sauce")
assert self.inventory_page.get_items_amount() == 6
Переход 4: WebDriverWait в каждом методе → в BasePage
Старое (из задания в лекции): WebDriverWait создаётся прямо в методе.
# Плохо: WebDriverWait создаётся при каждом вызове
class LoginPage:
def get_username_input(self, driver):
return WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "user-name"))
)
Современно:
self.wait создаётся один раз в BasePage.__init__ и переиспользуется.
# Хорошо: self.wait создаётся один раз в 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))