Утверждения
Полный справочник по утверждениям (assertions) в Playwright Python: expect, мягкие проверки, таймауты, сравнение скриншотов и expect.poll.
Обзор утверждений в Playwright
Playwright предоставляет мощную систему утверждений через функцию expect(). Все утверждения автоматически повторяются до достижения таймаута — это означает, что проверка будет успешной, как только условие будет выполнено, без необходимости писать явные ожидания. Это ключевое отличие от традиционных assert-библиотек: Playwright ждёт, пока состояние станет ожидаемым.
Важно: Все утверждения в Playwright являются асинхронными и используют механизм автоматического повторного запроса (auto-retry). По умолчанию таймаут составляет 5 секунд, но его можно изменить глобально или для конкретного утверждения.
Утверждения для страницы (Page)
expect(page).to_have_title()
Проверяет заголовок страницы (содержимое тега <title>).
from playwright.sync_api import expect expect(page).to_have_title("Главная страница") expect(page).to_have_title(re.compile(r"Страница \d+")) expect(page).to_have_title("Точный заголовок", exact=True) expect(page).to_have_title("Медленная загрузка", timeout=10000)
expect(page).to_have_url()
Проверяет текущий URL страницы.
expect(page).to_have_url("https://example.com/dashboard") expect(page).to_have_url(re.compile(r"example\.com/\d+")) expect(page).to_have_url("**/dashboard") # glob-паттерн expect(page).to_have_url("https://example.com", timeout=5000)
Утверждения для локаторов (Locator)
to_be_visible() / to_be_hidden()
Проверяют видимость элемента на странице.
expect(page.get_by_role("button", name="Submit")).to_be_visible() expect(page.get_by_role("dialog")).to_be_hidden() expect(page.get_by_text("Спасибо")).to_be_visible(timeout=3000) expect(page.locator(".spinner")).to_be_hidden(timeout=10000)
to_be_enabled() / to_be_disabled()
Проверяют, включён или отключён элемент (атрибут disabled).
expect(page.get_by_role("button", name="Отправить")).to_be_enabled() expect(page.get_by_placeholder("Введите имя")).to_be_enabled() expect(page.get_by_role("button", name="Удалить")).to_be_disabled() expect(page.get_by_role("textbox", name="Email")).to_be_enabled(timeout=5000)
to_be_checked()
Проверяет, что чекбокс или радиокнопка отмечены.
expect(page.get_by_role("checkbox", name="Согласен")).to_be_checked() expect(page.get_by_role("radio", name="Вариант 1")).to_be_checked() expect(page.get_by_role("checkbox")).not_.to_be_checked() # не отмечен
to_be_focused()
Проверяет, что элемент находится в фокусе.
expect(page.get_by_label("Имя")).to_be_focused() page.get_by_label("Email").focus() expect(page.get_by_label("Email")).to_be_focused()
to_be_empty()
Проверяет, что элемент пуст (не содержит дочерних узлов или текста).
expect(page.get_by_role("listitem").first).to_be_empty() expect(page.locator(".notifications")).to_be_empty() expect(page.get_by_role("textbox")).to_be_empty() # пустое поле ввода
to_have_text() / to_contain_text()
to_have_text() проверяет текстовое содержимое элемента. Поддерживает точное совпадение и частичное. to_contain_text() проверяет, что элемент содержит указанный текст где-либо внутри себя.
# Точное совпадение текста expect(page.get_by_role("heading", name="Добро пожаловать")).to_have_text("Добро пожаловать") # Частичное совпадение (по умолчанию) expect(page.get_by_role("button")).to_have_text("Отправить") # Совпадение с регулярным выражением expect(page.locator(".price")).to_have_text(re.compile(r"\d+\.\d{2} руб")) # Точное совпадение (exact=True игнорирует пробелы по краям) expect(page.get_by_role("heading")).to_have_text("Заголовок", exact=True) # to_contain_text — текст содержится где-то внутри элемента expect(page.locator(".card")).to_contain_text("специальное предложение") # Проверка нескольких элементов по тексту expect(page.get_by_role("listitem")).to_have_text(["Пункт 1", "Пункт 2", "Пункт 3"]) # Использование регулярных выражений в списке expect(page.get_by_role("row")).to_have_text([ re.compile(r"ID-\d+"), "Активен", re.compile(r"\d{2}\.\d{2}\.\d{4}") ])
to_have_value() / to_have_values()
Проверяют значение поля ввода (value) или выбранные опции в <select multiple>.
# Проверка значения поля ввода expect(page.get_by_label("Email")).to_have_value("user@example.com") # Проверка выбранного значения в select expect(page.get_by_role("combobox", name="Страна")).to_have_value("RU") # Проверка множественного выбора (select multiple) expect(page.get_by_role("listbox")).to_have_values(["red", "blue"]) # С регулярным выражением expect(page.get_by_label("Телефон")).to_have_value(re.compile(r"^\+7 \d{3}"))
to_have_attribute()
Проверяет значение атрибута элемента.
expect(page.get_by_role("link", name="Подробнее")).to_have_attribute("href", "/details") expect(page.get_by_role("img", name="Лого")).to_have_attribute("src", re.compile(r"logo.*\.png")) expect(page.get_by_test_id("submit-btn")).to_have_attribute("type", "submit") expect(page.locator("input")).to_have_attribute("placeholder", "Введите email")
to_have_class()
Проверяет CSS-классы элемента.
expect(page.get_by_role("button")).to_have_class("btn btn-primary") expect(page.get_by_role("button")).to_have_class(re.compile(r"btn-.*")) expect(page.locator(".nav-item")).to_have_class(["nav-item", "active"])
to_have_count()
Проверяет количество элементов, соответствующих локатору.
expect(page.get_by_role("listitem")).to_have_count(5) expect(page.get_by_role("row")).to_have_count(11) # 10 строк + заголовок expect(page.get_by_role("button")).to_have_count(3, timeout=10000) expect(page.locator(".error-message")).to_have_count(0) # нет ошибок
to_have_css()
Проверяет CSS-свойства элемента.
expect(page.get_by_role("button", name="Submit")).to_have_css("background-color", "rgb(0, 123, 255)") expect(page.get_by_role("heading")).to_have_css("font-size", "24px") expect(page.locator(".error")).to_have_css("color", re.compile(r"^rgb\(255,")) expect(page.get_by_test_id("modal")).to_have_css("display", "none")
to_have_id()
Проверяет значение атрибута id элемента.
expect(page.get_by_role("button")).to_have_id("submit-btn") expect(page.locator("header")).to_have_id("main-header") expect(page.get_by_role("form")).to_have_id(re.compile(r"form-\d+"))
to_have_js_property()
Проверяет JavaScript-свойство DOM-элемента.
expect(page.get_by_role("textbox")).to_have_js_property("type", "email") expect(page.locator("video")).to_have_js_property("paused", True) expect(page.get_by_role("button")).to_have_js_property("disabled", False) # Проверка сложного свойства expect(page.locator("progress")).to_have_js_property("value", 42)
Soft assertions — мягкие утверждения
По умолчанию, если утверждение не проходит, тест немедленно завершается ошибкой. Мягкие утверждения (soft assertions) позволяют продолжить выполнение теста и собрать все ошибки, а затем сообщить о них в конце.
# Мягкое утверждение — тест продолжается даже при ошибке expect(page.get_by_role("heading")).to_be_visible() expect(page.get_by_text("Добро пожаловать")).to_be_visible(timeout=5000) # мягкая проверка # Использование expect.with_options expect.configure(timeout=10000) expect(page.get_by_text("Загрузка завершена")).to_be_visible()
Мягкие утверждения полезны при проверке множества независимых условий на одной странице — вы получите полный список проблем, а не только первую ошибку.
Таймауты утверждений
По умолчанию все утверждения имеют таймаут 5 секунд. Таймаут можно изменить:
- Глобально — в конфигурации тестов Playwright
- Для всех expect — через
expect.set_options(timeout=10000) - Для конкретного утверждения — параметром
timeout
# Глобальная настройка таймаута для всех утверждений expect.configure(timeout=10000) # Таймаут для конкретного утверждения (переопределяет глобальный) expect(page.get_by_text("Медленно загружаемый текст")).to_be_visible(timeout=15000) # Таймаут в конфигурации playwright (playwright.config) # expect: { timeout: 10000 }
expect.poll() — Повторная проверка с интервалом
expect.poll() позволяет повторно выполнять функцию или получать значение до тех пор, пока оно не удовлетворит условию. Это полезно для проверок, которые не вписываются в стандартные утверждения.
import re from playwright.sync_api import expect # Проверка с интервалом: ждём, пока значение счётчика не станет 100 expect.poll( lambda: page.locator(".counter").text_content(), timeout=10000, interval=500 ).to_have_text("100") # Проверка с регулярным выражением expect.poll( lambda: page.evaluate("document.title"), timeout=5000 ).to_have_text(re.compile(r"Готово \d+%")) # Проверка JS-свойства с интервалом expect.poll( lambda: page.evaluate("window.loadingProgress"), interval=200, timeout=30000 ).to_have_value(100) # poll с кастомной функцией проверки expect.poll( lambda: page.locator(".status").get_attribute("data-state"), timeout=5000 ).to_be("loaded") # to_be для произвольных значений
Совет: expect.poll() принимает параметры timeout (максимальное время ожидания) и interval (интервал между попытками, по умолчанию 100 мс). Функция выполняется повторно, пока не будет достигнут таймаут или условие не будет выполнено.
Сравнение скриншотов
expect(page).to_have_screenshot() делает скриншот страницы или элемента и сравнивает его с эталонным изображением. Это мощный инструмент для визуального регрессионного тестирования.
# Сравнение полного скриншота страницы с эталоном expect(page).to_have_screenshot() # Сравнение скриншота с именованным эталоном expect(page).to_have_screenshot("login-page.png") # Сравнение скриншота конкретного элемента expect(page.get_by_role("form", name="Login")).to_have_screenshot() # Скриншот с маскированием динамических областей expect(page).to_have_screenshot(mask=[page.locator(".timestamp")]) # Скриншот с опциями expect(page).to_have_screenshot( "dashboard.png", full_page=True, animations="disabled", timeout=15000, max_diff_pixel_ratio=0.01 # допускается 1% различий ) # Скриншот с маскированным элементом expect(page).to_have_screenshot(mask=[page.locator(".banner")], mask_color="#FF0000") # Скриншот элемента с обрезкой (clip) expect(page.locator(".chart")).to_have_screenshot( "chart.png", max_diff_pixels=100 ) # Обновление эталонов (режим записи) # При запуске с --update-snapshots все скриншоты будут перезаписаны expect(page).to_have_screenshot("new-reference.png")
Важно: Скриншоты по умолчанию сохраняются в директории test-results или в кастомной папке, указанной в конфигурации. При первом запуске эталонный скриншот создаётся автоматически. При последующих запусках происходит сравнение с эталоном. Для обновления эталонов используйте флаг --update-snapshots.
Параметры to_have_screenshot()
| Параметр | Описание |
|---|---|
name | Имя файла эталонного скриншота |
full_page | Скриншот всей страницы (включая прокручиваемую часть) |
clip | Обрезка: {"x": 0, "y": 0, "width": 100, "height": 100} |
mask | Список локаторов для маскирования |
mask_color | Цвет маски (по умолчанию #FF00FF) |
animations | Обработка анимации: "disabled", "allow" |
max_diff_pixel_ratio | Максимальная доля отличающихся пикселей (0.0–1.0) |
max_diff_pixels | Максимальное количество отличающихся пикселей |
threshold | Порог сравнения пикселей (0–1, по умолчанию 0.2) |
timeout | Таймаут ожидания (мс) |
Практические примеры
Полный набор проверок страницы
def test_page_assertions(page): page.goto("https://example.com/dashboard") # Проверки страницы expect(page).to_have_title("Панель управления") expect(page).to_have_url(re.compile(r"example\.com/dashboard")) # Проверки видимости элементов expect(page.get_by_role("heading", name="Добро пожаловать")).to_be_visible() expect(page.get_by_text("Загрузка...")).to_be_hidden() # Проверки состояния expect(page.get_by_role("button", name="Сохранить")).to_be_enabled() expect(page.get_by_role("button", name="Удалить")).to_be_disabled() # Проверка формы expect(page.get_by_label("Email")).to_have_value("admin@example.com") expect(page.get_by_label("Email")).to_be_focused() # Проверка списка expect(page.get_by_role("listitem")).to_have_count(5) expect(page.get_by_role("listitem").first).to_have_text("Задача #1")
Мягкие проверки в одном тесте
def test_multiple_assertions(page: Page): page.goto("https://example.com/profile") # Собираем все ошибки, не прерывая тест errors = [] try: expect(page.get_by_role("heading", name="Профиль")).to_be_visible(timeout=3000) except AssertionError as e: errors.append(f"Заголовок не виден: {e}") try: expect(page.get_by_label("Имя")).to_have_value("Иван") except AssertionError as e: errors.append(f"Имя не совпадает: {e}") if errors: raise AssertionError("\n".join(errors))
Сравнение скриншотов в CI
def test_visual_regression(page): page.goto("https://example.com/home") # Маскируем динамические элементы (дата, время, ID сессии) expect(page).to_have_screenshot( "home-page.png", mask=[ page.locator(".session-id"), page.locator(".current-date"), page.locator(".random-banner") ], max_diff_pixel_ratio=0.005 # допускаем 0.5% различий )
Примечание: Playwright использует pixelmatch для сравнения скриншотов. При первом запуске тестов со скриншотами эталонные изображения создаются автоматически. Для обновления эталонов запустите тесты с флагом --update-snapshots или -u.
Заключение
Утверждения Playwright — это мощная и гибкая система проверок, которая автоматически ожидает выполнения условий. Основные преимущества:
- Автоматический повтор — утверждения повторяются до достижения таймаута
- Читаемые сообщения об ошибках — понятные описания, что пошло не так
- Мягкие проверки — можно собрать все ошибки без остановки теста
- expect.poll() — для кастомных проверок с интервалом
- Сравнение скриншотов — визуальное регрессионное тестирование
- Гибкая настройка таймаутов — глобально, для всех expect или для конкретного утверждения