Playwright Python — справочник На главный сайт

Что такое Page Object Model?

Page Object Model (POM) — это паттерн проектирования тестовой автоматизации, при котором каждая страница или значимый компонент веб-приложения представляется отдельным классом. Класс страницы инкапсулирует селекторы (локаторы) и методы взаимодействия с этой страницей. Тесты используют эти классы, оставаясь независимыми от деталей разметки.

Основная идея: при изменении вёрстки страницы достаточно обновить только класс страницы, а не все тесты, которые с ней работают.

Структура класса страницы

Каждый класс POM содержит:

  • Конструктор __init__(self, page: Page) — принимает объект страницы Playwright
  • Локаторы — объявляются как атрибуты через page.get_by_role(), page.locator() и другие методы
  • Методы действийnavigate(), search(), get_result() и т.д.
  • Методы проверок — возвращают состояние элементов для assert/expect
from playwright.sync_api import Page


class SearchPage:
    """Page Object для страницы поиска."""

    def __init__(self, page: Page):
        self.page = page
        self.search_input = page.get_by_role("textbox", name="Поиск")
        self.search_button = page.get_by_role("button", name="Найти")
        self.results = page.get_by_role("listitem")
        self.no_results_msg = page.get_by_text("Ничего не найдено")

    def navigate(self) -> None:
        self.page.goto("https://example.com/search")

    def search(self, query: str) -> None:
        self.search_input.fill(query)
        self.search_button.click()
        self.page.wait_for_load_state("networkidle")

    def get_result(self, index: int = 0):
        return self.results.nth(index)

    def has_results(self) -> bool:
        return self.results.count() > 0

    def no_results_message_visible(self) -> bool:
        return self.no_results_msg.is_visible()

Использование POM в тестах pytest

В pytest класс POM удобно использовать через фикстуру page, которую предоставляет плагин pytest-playwright.

from search_page import SearchPage


def test_search_finds_results(page):
    search_page = SearchPage(page)
    search_page.navigate()
    search_page.search("Playwright Python")
    result = search_page.get_result(0)
    expect(result).to_be_visible()
    expect(result).to_contain_text("Playwright")


def test_search_empty_query(page):
    search_page = SearchPage(page)
    search_page.navigate()
    search_page.search("")
    expect(search_page.no_results_msg).to_be_visible()

Продвинутый POM: композиция и фрагменты

Для сложных страниц можно выделять повторяющиеся компоненты в отдельные классы-фрагменты.

class HeaderFragment:
    def __init__(self, page: Page):
        self.logo = page.get_by_role("link", name="Главная")
        self.login_btn = page.get_by_role("button", name="Войти")
        self.cart_link = page.get_by_role("link", name="Корзина")

    def go_to_cart(self):
        self.cart_link.click()

    def is_logged_in(self) -> bool:
        return not self.login_btn.is_visible()


class MainPage:
    def __init__(self, page: Page):
        self.page = page
        self.header = HeaderFragment(page)

    def navigate(self):
        self.page.goto("https://example.com")

Асинхронный POM (async/await)

Playwright поддерживает как синхронный, так и асинхронный API. Для async-проектов классы POM пишутся с async/await.

from playwright.async_api import Page as AsyncPage


class AsyncSearchPage:
    def __init__(self, page: AsyncPage):
        self.page = page
        self.search_input = page.get_by_role("textbox", name="Поиск")
        self.search_button = page.get_by_role("button", name="Найти")

    async def navigate(self):
        await self.page.goto("https://example.com/search")

    async def search(self, query: str):
        await self.search_input.fill(query)
        await self.search_button.click()
        await self.page.wait_for_load_state("networkidle")

Совет: Выбор между синхронным и асинхронным POM зависит от проекта. Для большинства тестов достаточно синхронного подхода. Асинхронный вариант полезен при параллельных операциях или интеграции с асинхронными фреймворками.

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

  • Переиспользование кода — методы страницы используются во всех тестах
  • Лёгкость поддержки — изменение селектора в одном месте, а не во всех тестах
  • Читаемость — тесты читаются как сценарии на естественном языке
  • Модульность — каждый класс отвечает за свою страницу или компонент
  • Снижение дублирования — общие проверки выносятся в методы класса

Важно: Не злоупотребляйте наследованием в POM. Предпочитайте композицию (вложение фрагментов) или делегирование. Глубокие иерархии наследования усложняют поддержку.

Рекомендации по организации

  • Храните каждый класс POM в отдельном файле
  • Используйте осмысленные имена методов: login(), add_to_cart(), get_total_price()
  • Возвращайте из методов другие Page Object для организации fluent-интерфейса
  • Не смешивайте бизнес-логику с техническими деталями
  • Для утверждений используйте expect() из Playwright
def test_checkout_flow(page):
    main_page = MainPage(page)
    main_page.navigate()
    main_page.header.go_to_cart()

    cart_page = CartPage(page)
    cart_page.proceed_to_checkout()

    checkout_page = CheckoutPage(page)
    checkout_page.fill_shipping_info("Иван", "ул. Ленина, 1")
    checkout_page.place_order()

    confirmation = OrderConfirmationPage(page)
    expect(confirmation.order_number).to_be_visible()