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

pytest-playwright — основной раннер

Playwright предоставляет официальную интеграцию с pytest через пакет pytest-playwright. Это самый удобный и рекомендуемый способ запуска тестов. Он предоставляет встроенные фикстуры, автоматическую настройку браузеров и богатые возможности конфигурации.

# Установка
pip install pytest-playwright
playwright install

Встроенные фикстуры: page, context, browser, playwright — доступны сразу после установки. Каждая фикстура создаётся заново для каждого теста, если не указано иное.

Базовый пример с pytest-playwright

# test_example.py
def test_login(page):
    page.goto("https://example.com/login")
    page.get_by_label("Email").fill("user@example.com")
    page.get_by_label("Пароль").fill("secret123")
    page.get_by_role("button", name="Войти").click()
    expect(page.get_by_text("Добро пожаловать")).to_be_visible()

Фикстуры pytest-playwright

page — изолированная страница

Каждый тест получает новую page (и новый контекст, и новый браузер). Это обеспечивает полную изоляцию.

context — браузерный контекст

Позволяет работать с контекстом напрямую, например, для тестирования нескольких вкладок.

def test_multi_tab(context):
    page1 = context.new_page()
    page2 = context.new_page()
    page1.goto("https://example.com")
    page2.goto("https://playwright.dev")

browser — браузерный процесс

Даёт доступ к запущенному браузеру. Полезно для создания контекстов с кастомными настройками.

def test_custom_context(browser):
    context = browser.new_context(
        viewport={"width": 1920, "height": 1080},
        locale="ru-RU"
    )
    page = context.new_page()
    page.goto("https://example.com")

playwright — корневой объект

Предоставляет доступ к Playwright API для запуска браузеров вручную.

def test_with_playwright(playwright):
    chromium = playwright.chromium
    browser = chromium.launch(headless=True)

Кастомные фикстуры

Вы можете создавать собственные фикстуры для переиспользования логики между тестами.

# conftest.py
import pytest
from playwright.sync_api import Page

@pytest.fixture
def auth_page(page: Page) -> Page:
    page.goto("https://example.com/login")
    page.get_by_label("Email").fill("admin@example.com")
    page.get_by_label("Пароль").fill("admin123")
    page.get_by_role("button", name="Войти").click()
    return page

# test_dashboard.py
def test_dashboard(auth_page):
    auth_page.goto("https://example.com/dashboard")
    expect(auth_page.get_by_text("Панель управления")).to_be_visible()

Совет: Используйте scope="session" для фикстур, которые должны создаваться один раз на всю сессию тестирования, например, для авторизованного контекста.

@pytest.fixture(scope="session")
def auth_context(browser):
    context = browser.new_context(storage_state="auth.json")
    yield context
    context.close()

def test_with_auth(auth_context):
    page = auth_context.new_page()
    page.goto("https://example.com/profile")

conftest.py — глобальные фикстуры и хуки

Файл conftest.py автоматически загружается pytest и позволяет определять глобальные фикстуры, хуки и переопределения. Размещайте его в корне тестовой директории.

# conftest.py — расширенный пример
import pytest
from playwright.sync_api import sync_playwright

@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
    return {
        "viewport": {"width": 1920, "height": 1080},
        "locale": "ru-RU",
        "timezone_id": "Europe/Moscow",
    }

def pytest_runtest_makereport(item, call):
    """Хук: скриншот при падении теста"""
    if call.when == "call" and call.excinfo is not None:
        page = item.funcargs.get("page")
        if page:
            page.screenshot(path="failure.png")

pytest.ini / pyproject.toml — конфигурация

Настройки pytest-playwright можно задать в pytest.ini или pyproject.toml.

pytest.ini

# pytest.ini
[pytest]
testpaths = tests
addopts = -v --headed --slowmo=100
filterwarnings =
    ignore::DeprecationWarning

pyproject.toml

# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --headed --slowmo=100"

Основные CLI-флаги pytest-playwright:

ФлагОписание
--headedЗапуск с видимым браузером
--browser=chromiumВыбор браузера (chromium/firefox/webkit)
--browser=allЗапуск во всех браузерах
--slowmo=100Задержка между действиями в мс
--device="iPhone 13"Эмуляция устройства
--video=onЗапись видео
--screenshot=only-on-failureСкриншоты только при падении
--tracing=onТрассировка

Параллельный запуск: pytest-xdist

Для ускорения тестов используйте плагин pytest-xdist. Playwright поддерживает параллельный запуск на уровне браузера.

# Установка
pip install pytest-xdist

# Запуск с 4 процессами
pytest -n auto

# Указать количество воркеров явно
pytest -n 4

Важно: При параллельном запуске каждый воркер использует отдельный браузер. Убедитесь, что тесты изолированы и не зависят от общего состояния.

Переиспользование браузера: --reuse

Флаг --reuse позволяет переиспользовать один экземпляр браузера между тестами, что ускоряет запуск при параллельном выполнении.

pytest --reuse --workers 4

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

Маркеры: skip_browser и only_browser

Playwright предоставляет маркеры для управления браузерами, в которых запускаются тесты.

import pytest

@pytest.mark.skip_browser("firefox")
def test_chromium_only(page):
    "Тест будет пропущен в Firefox"
    page.goto("https://example.com")

@pytest.mark.only_browser("chromium")
def test_only_chromium(page):
    "Запускается только в Chromium"
    page.goto("https://example.com")

Маркеры работают только при запуске с --browser=all. Иначе Playwright запускает браузер по умолчанию (chromium) без учёта маркеров.

Sharding — распределение тестов в CI

Sharding позволяет разделить тесты на несколько групп (шардов) для параллельного выполнения на разных CI-агентах.

# CI-задача 1: запускает первую четверть тестов
pytest --shard=1/4

# CI-задача 2: запускает вторую четверть
pytest --shard=2/4

# CI-задача 3: третья четверть
pytest --shard=3/4

# CI-задача 4: четвёртая четверть
pytest --shard=4/4

Sharding особенно полезен при ограниченном времени выполнения CI-пайплайна. Каждый шард выполняется на отдельном агенте, и общее время сокращается пропорционально количеству шардов.

pytest --max-failures — остановка при первом падении

Флаг --max-failures останавливает тестовый прогон после указанного количества упавших тестов.

pytest --max-failures=1

Это удобно для быстрой обратной связи: если один тест упал, нет смысла запускать остальные — ошибка уже есть и нужно её исправить.

HTML-отчёты: pytest-html

Плагин pytest-html генерирует детальный HTML-отчёт о результатах тестирования.

# Установка
pip install pytest-html

# Запуск с генерацией отчёта
pytest --html=report.html --self-contained-html

Флаг --self-contained-html встраивает все ресурсы (CSS, изображения) прямо в HTML-файл, что удобно для хранения артефактов CI.

Allure-отчёты

Allure Framework предоставляет богатые интерактивные отчёты с историей прогонов, графиками и вложениями.

# Установка
pip install allure-pytest

# Запуск с сохранением результатов Allure
pytest --alluredir=./allure-results

# Генерация и открытие отчёта
allure serve ./allure-results

Совет: Allure поддерживает аннотации для обогащения отчётов: @allure.title, @allure.description, @allure.feature, @allure.story и @allure.step.

import allure

@allure.feature("Авторизация")
@allure.story("Успешный вход")
def test_login_success(page):
    with allure.step("Открыть страницу логина"):
        page.goto("https://example.com/login")
    with allure.step("Заполнить учётные данные"):
        page.get_by_label("Email").fill("user@example.com")
        page.get_by_label("Пароль").fill("secret123")
    with allure.step("Нажать кнопку входа"):
        page.get_by_role("button", name="Войти").click()
    with allure.step("Проверить успешный вход"):
        expect(page.get_by_text("Добро пожаловать")).to_be_visible()

BDD: pytest-bdd с Playwright

Playwright отлично интегрируется с pytest-bdd для Behavior-Driven Development. Вы пишете сценарии на Gherkin, а шаги реализуете с использованием Playwright.

# login.feature
Feature: Авторизация
  Scenario: Успешный вход
    Given пользователь открыл страницу логина
    When пользователь вводит email "user@example.com"
    And пользователь вводит пароль "secret123"
    And пользователь нажимает "Войти"
    Then пользователь видит приветствие
# test_login.py
from pytest_bdd import scenarios, given, when, then

scenarios("login.feature")

@given("пользователь открыл страницу логина")
def open_login(page):
    page.goto("https://example.com/login")

@when('пользователь вводит email "<email>"')
def enter_email(page, email):
    page.get_by_label("Email").fill(email)

@when('пользователь вводит пароль "<password>"')
def enter_password(page, password):
    page.get_by_label("Пароль").fill(password)

@when('пользователь нажимает "Войти"')
def click_login(page):
    page.get_by_role("button", name="Войти").click()

@then("пользователь видит приветствие")
def see_welcome(page):
    expect(page.get_by_text("Добро пожаловать")).to_be_visible()

unittest — Playwright как библиотека

Playwright можно использовать с классическим unittest без pytest. В этом случае вы управляете жизненным циклом браузера вручную.

import unittest
from playwright.sync_api import sync_playwright

class TestLogin(unittest.TestCase):

    def setUp(self):
        self.playwright = sync_playwright().start()
        self.browser = self.playwright.chromium.launch()
        self.context = self.browser.new_context()
        self.page = self.context.new_page()

    def tearDown(self):
        self.browser.close()
        self.playwright.stop()

    def test_login_page(self):
        self.page.goto("https://example.com/login")
        self.page.get_by_label("Email").fill("user@example.com")
        self.page.get_by_role("button", name="Войти").click()
        self.assert_true(
            self.page.get_by_text("Добро пожаловать").is_visible()
        )

if __name__ == "__main__":
    unittest.main()

Недостаток unittest: Вам нужно вручную управлять браузером, контекстом и страницами. Также отсутствуют встроенные фикстуры, автоматический скриншот при падении и другие удобства pytest-playwright.

Заключение

pytest-playwright — самый мощный и удобный способ запуска тестов. Используйте встроенные фикстуры для изоляции тестов, кастомные фикстуры для переиспользования логики, параллельный запуск через xdist для ускорения, а BDD-подход для улучшения читаемости сценариев. Для CI-пайплайнов применяйте шардирование, HTML- или Allure-отчёты.