Автоматизация тестирования на Python с Playwright и Pytest

Курс охватывает современные практики UI-автоматизации, включая работу с локаторами, ассертами и паттерном Page Object Model [vc.ru](https://vc.ru/dev/1993152-kak-pisat-ui-avto-testy-na-python-s-playwright). Вы научитесь настраивать фикстуры, интегрировать тесты в CI/CD и генерировать отчеты Allure [habr.com](https://habr.com/ru/articles/896936).

1. Основы Pytest и взаимодействие с элементами в Playwright

Основы Pytest и взаимодействие с элементами в Playwright

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

Роль Pytest в автоматизации

Playwright сам по себе — это библиотека для управления браузером. Он умеет «кликать» и «печатать», но он не знает, что такое «тест», «успех» или «провал». Эту роль берет на себя Pytest.

Pytest — это стандарт де-факто в мире тестирования на Python. Он отвечает за:

  • Поиск тестов (Test Discovery): находит файлы и функции, которые нужно запустить.
  • Запуск (Execution): выполняет код в нужном порядке.
  • Отчетность (Reporting): сообщает, какие проверки прошли, а какие упали.
  • Правила именования

    Чтобы Pytest автоматически нашел ваши тесты, нужно соблюдать простые соглашения: * Файлы должны начинаться с test_ или заканчиваться на _test.py (например, test_login.py). * Функции внутри файлов должны начинаться с префикса test_ (например, def test_submit_form():).

    Пример простейшего теста:

    Локаторы в Playwright: Как найти элемент?

    Самая частая задача в UI-тестах — найти кнопку, поле ввода или текст. Для этого используются локаторы. От качества локаторов зависит стабильность ваших тестов. Если вы привяжетесь к случайному CSS-классу, который разработчики изменят завтра, тест упадет.

    Философия User-Facing Locators

    Playwright продвигает подход, ориентированный на пользователя. Тест должен взаимодействовать со страницей так, как это делает реальный человек. Пользователь не ищет кнопку по div > span:nth-child(3), он ищет кнопку с текстом «Войти».

    Согласно документации и практике сообщества, приоритет выбора локаторов выглядит так:

  • page.get_by_role() — самый надежный метод. Использует семантические роли ARIA (button, link, heading).
  • page.get_by_text() — поиск по видимому тексту.
  • page.get_by_label() — для полей ввода, связанных с тегом <label>.
  • page.get_by_placeholder() — поиск по атрибуту placeholder.
  • И только если эти методы не подходят, стоит использовать CSS (page.locator("css-selector")) или XPath.

    > Playwright — это мощное средство автоматизации, дающее широкий набор возможностей для взаимодействия с браузерами, и встроенный класс expect упрощает работу с ассертами. > > habr.com

    Примеры использования

    Предположим, у нас есть кнопка <button>Отправить</button>.

    Плохой вариант (хрупкий):

    Хороший вариант (устойчивый):

    Если на странице несколько элементов с одинаковой ролью, Playwright выдаст ошибку. Это полезно, так как заставляет вас уточнять критерии поиска, делая тест более предсказуемым.

    Взаимодействие с элементами (Actions)

    После того как элемент найден, с ним нужно выполнить действие. Playwright автоматически ждет, пока элемент станет «активным» (Actionability checks). Это означает, что вам не нужно писать time.sleep() — фреймворк сам убедится, что кнопка видима, не перекрыта другим элементом и готова к клику.

    Основные команды

    * Клик: * Ввод текста: Используйте fill() вместо посимвольного ввода, если не требуется эмуляция нажатия клавиш. * Нажатие клавиш: * Выбор из списка:

    Ожидания (Auto-waiting)

    Одной из главных проблем старых инструментов (например, Selenium) была необходимость ручных ожиданий. Как отмечают в сообществе, Playwright решает эту проблему архитектурно.

    > У Playwright есть много крутых фич, с которыми он рвет Selenium в пух... Playwright удобнее и современнее. > > vc.ru

    Перед выполнением действия Playwright проверяет, что элемент:

  • Присутствует в DOM.
  • Видим (visible).
  • Не анимируется (stable).
  • Может получить событие (enabled).
  • Проверки (Assertions) с использованием expect

    Тест без проверки — это просто скрипт. В Pytest есть стандартный оператор assert, но для UI-тестов его недостаточно. Веб-страницы динамичны: элемент может появиться не сразу.

    Для этого Playwright предоставляет синхронный модуль expect. Он работает по принципу Web-First Assertions: если условие не выполняется сразу, он будет повторять проверку (retry) в течение заданного тайм-аута (по умолчанию 5 секунд).

    Синтаксис

    Почему не обычный assert?

    Рассмотрим пример:

    Если в момент выполнения этой строки текст еще не подгрузился (например, спиннер крутится 0.5 секунды), тест упадет мгновенно. expect же будет опрашивать элемент до истечения тайм-аута, делая тест стабильным без лишних пауз.

    Собираем всё вместе: Первый полноценный тест

    Напишем тест для сценария логина. Мы будем использовать фикстуру page, которую предоставляет плагин pytest-playwright (подробнее о фикстурах мы поговорим в следующих статьях, пока просто знайте, что она дает готовый объект страницы).

    Файл: test_login_scenario.py

    Запуск теста

    В терминале выполните команду: pytest

    Чтобы увидеть браузер вживую (по умолчанию тесты запускаются в headless режиме, то есть без интерфейса), добавьте флаг --headed: pytest --headed

    Чтобы замедлить выполнение для отладки, используйте --slowmo (в миллисекундах): pytest --headed --slowmo 1000

    Итоги

  • Pytest управляет запуском тестов. Файлы и функции должны начинаться с префикса test_.
  • Используйте User-Facing Locators (get_by_role, get_by_text) как основной способ поиска элементов. Это делает тесты устойчивыми к изменениям верстки.
  • Playwright имеет встроенный механизм Auto-waiting: он сам ждет, пока элемент станет доступен для клика или ввода.
  • Для проверок всегда используйте expect(). Это обеспечивает автоматические повторные попытки (retries) при динамическом контенте, в отличие от стандартного assert.
  • Метод fill() предпочтительнее для быстрого заполнения форм, чем эмуляция нажатия клавиш.
  • 2. Архитектура тестов и паттерн Page Object Model

    Архитектура тестов и паттерн Page Object Model

    В предыдущей статье мы научились писать простые линейные тесты: открывали браузер, находили элементы и кликали по ним. Это отлично работает, когда у вас 5–10 тестов. Но представьте, что вы работаете над крупным проектом, где тестов сотни. Вдруг разработчики меняют ID кнопки «Войти». В линейном подходе вам придется открывать каждый из 100 файлов и менять локатор вручную. Это ад поддержки.

    Сегодня мы перейдем на профессиональный уровень и разберем архитектурный паттерн Page Object Model (POM), который является стандартом индустрии.

    Проблема «Спагетти-кода»

    Когда мы пишем локаторы (page.locator("#login")) и действия (.click()) прямо внутри тестовой функции, мы смешиваем логику теста (что мы проверяем) и реализацию взаимодействия (как мы это делаем).

    Это приводит к двум проблемам:

  • Дублирование кода: Локатор кнопки логина повторяется в каждом тесте, где нужна авторизация.
  • Хрупкость: Любое изменение верстки ломает все тесты сразу.
  • Что такое Page Object Model (POM)?

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

    > Основная идея состоит в том, чтобы разделить логику тестов от реализации. Каждую веб-страницу проекта можно описать в виде объекта класса. Взаимодействие пользователя описываются в методах класса, а в тестах остается только бизнес-логика. > > habr.com

    Основные принципы POM:

    * Страница = Класс. Главная страница — это класс HomePage, страница корзины — CartPage. * Действие = Метод. Если пользователь может нажать кнопку «Войти», у класса должен быть метод login(). * Локатор = Атрибут (или скрытая деталь). Тест не должен знать, какой CSS-селектор у кнопки. Он просто вызывает метод.

    Структура проекта

    Для внедрения POM нам нужно организовать файлы. Типичная структура современного проекта на Python + Playwright выглядит так:

    Реализация: От теории к коду

    Давайте перепишем наш тест логина из прошлой статьи, используя POM.

    Шаг 1. Base Page

    Сначала создадим родительский класс BasePage. Он будет хранить экземпляр page (объект Playwright) и общие методы, доступные на любой странице (например, открытие URL).

    Файл: pages/base_page.py

    Шаг 2. Login Page

    Теперь создадим класс для конкретной страницы. Здесь мы инкапсулируем (скроем) локаторы.

    Файл: pages/login_page.py

    Обратите внимание: методы называются понятным языком (login), а не техническим (fill_field_and_click).

    Шаг 3. Обновленный тест

    Теперь наш тест становится чистым и читаемым. Он читается как сценарий на английском языке.

    Файл: tests/test_login.py

    Математика выгоды: Зачем нам это нужно?

    Многие новички считают, что создание классов — это лишняя трата времени. Давайте посчитаем экономию времени при поддержке тестов, если на сайте изменился всего один селектор кнопки.

    Предположим, у нас есть 50 тестов, использующих логин. Время на исправление одного места в коде — 2 минуты.

    Без использования POM (правка в каждом файле):

    где — время правки одного файла, — количество файлов. Итого мы тратим 1 час 40 минут на мелкую правку.

    С использованием POM (правка в одном классе):

    где — время правки, — количество мест, где хранится локатор.

    Разница колоссальная. Архитектура экономит часы вашей жизни.

    Фикстуры: Убираем инициализацию

    В примере выше мы писали login_page = LoginPage(page) внутри теста. Если тестов много, это тоже дублирование. Pytest позволяет вынести создание объектов в фикстуры.

    Файл: tests/conftest.py

    Теперь тест становится еще лаконичнее:

    Pytest сам найдет фикстуру с именем login_page, создаст экземпляр класса и передаст его в тест.

    Лучшие практики 2026 года

    Согласно современным подходам к автоматизации, стоит придерживаться следующих правил:

  • Fluent Interface (Цепочки вызовов). Методы страницы могут возвращать self, чтобы можно было писать page.type_username().type_password().click_login(). Однако в Python это используется реже, чем в Java.
  • Ассерты в тестах, а не в страницах. Page Object должен предоставлять методы для получения состояния (текста, видимости), но решать, прошел тест или нет, должен сам тест. Это делает Page Object переиспользуемым.
  • Компоненты (Page Components). Если у вас есть хедер, футер или таблица, которые повторяются на всех страницах, не дублируйте их код. Выделите их в отдельные классы и подключайте к страницам.
  • > Цель статьи — не просто показать работу с этими паттернами, а разобрать их «на атомы» и объяснить, как они помогают сделать тесты масштабируемыми. > > vc.ru

    Итоги

  • Page Object Model (POM) разделяет код тестов и код взаимодействия со страницей. Это делает тесты читаемыми и легкими в поддержке.
  • Создавайте BasePage для общих методов и отдельные классы для каждой страницы приложения.
  • Локаторы должны быть спрятаны внутри классов страниц. Тест не должен знать о CSS или XPath.
  • Используйте фикстуры Pytest (conftest.py) для инициализации страниц, чтобы не захламлять код тестов.
  • Экономия времени при поддержке тестов с POM достигает десятков часов, так как изменения вносятся только в одном месте.