Контексты, ожидания и утверждения
Updated Jul 2026
Три концепции формируют основу преимущества надёжности Playwright перед предыдущими инструментами: контексты браузера для изоляции, автоожидание для устранения ошибок тайминга и web-first утверждения, которые повторяются до выполнения условия. Освойте эти три концепции — и большинство проблем нестабильности исчезнет.
Контексты браузера
Контекст браузера — это легковесная изолированная сессия, подобная окну инкогнито. Каждый контекст имеет собственные cookies, localStorage и кэш. В отличие от Selenium, где каждый тест обычно запускает новый процесс браузера, Playwright создаёт контексты внутри одного экземпляра браузера.
// Каждый тест автоматически получает свой контекст в @playwright/test
test('пользователь A видит свою панель', async ({ page }) => {
// `page` принадлежит свежему контексту — изолирован от других тестов
});
// Ручное создание контекстов для многопользовательских сценариев
test('два пользователя сотрудничают', async ({ browser }) => {
const adminContext = await browser.newContext();
const userContext = await browser.newContext();
const adminPage = await adminContext.newPage();
const userPage = await userContext.newPage();
// Админ и пользователь имеют полностью отдельные сессии
await adminPage.goto('/admin');
await userPage.goto('/dashboard');
});
Почему контексты важны
| Подход Selenium | Подход Playwright |
|---|---|
| Один браузер на тест (медленный запуск) | Один браузер, много контекстов (быстро) |
| Общее состояние просачивается между тестами | Полная изоляция для каждого контекста |
| Параллельность = несколько процессов браузера | Параллельность = несколько контекстов в одном браузере |
| Очистка cookies между тестами — вручную | Контексты одноразовые — очистка не нужна |
Автоожидание
В Selenium источник нестабильности тестов №1 — тайминг: тест пытается взаимодействовать с элементом до его готовности. Playwright устраняет это автоожиданием перед каждым действием.
Когда вы вызываете page.click('button'), Playwright автоматически ожидает, что элемент:
- Прикреплён к DOM
- Видим (не скрыт CSS)
- Стабилен (не анимируется)
- Активен (не отключён)
- Принимает события (не перекрыт другим элементом)
Только после этого выполняется клик. Если любое условие не выполнено, Playwright повторяет попытки до таймаута.
// Playwright: просто кликните — автоожидание обработает тайминг
await page.getByRole('button', { name: 'Submit' }).click();
// Selenium: необходимо вручную ждать, затем кликнуть
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//button[text()='Submit']"))
).click()
Что покрывает автоожидание
| Действие | Ожидает |
|---|---|
click() |
Прикреплён, видим, стабилен, активен, принимает события |
fill() |
Прикреплён, видим, активен, редактируем |
check() |
Прикреплён, видим, стабилен, активен |
selectOption() |
Прикреплён, видим, активен |
textContent() |
Прикреплён |
isVisible() |
Ничего (возвращает немедленно) |
Когда всё ещё нужны явные ожидания
Автоожидание обрабатывает взаимодействия, но иногда нужно дождаться условия перед продолжением:
// Ожидание конкретного ответа после запуска действия
await page.waitForResponse('**/api/orders');
// Ожидание навигации
await page.waitForURL('**/dashboard');
// Ожидание исчезновения элемента (спиннер загрузки)
await page.getByTestId('spinner').waitFor({ state: 'hidden' });
Web-First утверждения
Утверждения expect() в Playwright являются «web-first» — они повторяются до выполнения условия или истечения таймаута. Это принципиально отличается от мгновенных утверждений, которые проверяют один раз и падают.
// Web-first: повторяет до появления текста (до 5 секунд по умолчанию)
await expect(page.getByRole('alert')).toHaveText('Сохранено успешно');
// Мгновенное утверждение: проверяет один раз, падает если не готово
// НЕ ДЕЛАЙТЕ ТАК:
const text = await page.getByRole('alert').textContent();
expect(text).toBe('Сохранено успешно'); // хрупко!
Распространённые web-first утверждения
// Состояние элемента
await expect(page.getByRole('button')).toBeVisible();
await expect(page.getByRole('button')).toBeEnabled();
await expect(page.getByTestId('input')).toHaveValue('hello');
await expect(page.getByRole('alert')).toHaveText('Успех');
// Состояние страницы
await expect(page).toHaveTitle(/Панель/);
await expect(page).toHaveURL(/\/dashboard/);
// Количество элементов
await expect(page.getByRole('listitem')).toHaveCount(5);
// CSS и атрибуты
await expect(page.getByTestId('status')).toHaveClass(/active/);
await expect(page.getByRole('link')).toHaveAttribute('href', '/about');
Таймаут утверждений
// Таймаут по умолчанию: 5 секунд. Переопределение для конкретного утверждения:
await expect(page.getByText('Отчёт готов')).toBeVisible({ timeout: 30_000 });
// Или установить глобально в конфигурации:
// expect: { timeout: 10_000 }
Всё вместе
test('добавление товара в корзину', async ({ page }) => {
await page.goto('/products');
// Автоожидание: кликает когда кнопка готова
await page.getByRole('button', { name: 'В корзину' }).first().click();
// Web-first утверждение: повторяет до отображения «1» в бейдже корзины
await expect(page.getByTestId('cart-count')).toHaveText('1');
// Навигация и проверка
await page.getByRole('link', { name: 'Корзина' }).click();
await expect(page).toHaveURL(/.*cart/);
await expect(page.getByRole('listitem')).toHaveCount(1);
});
Никаких явных ожиданий. Никаких sleep-операторов. Никаких проблем с таймингом. Автоожидание и web-first утверждения обрабатывают всю синхронизацию.
Ключевые выводы
- Контексты браузера обеспечивают легковесные изолированные сессии — нет утечек состояния между тестами
- Автоожидание проверяет готовность (видимость, стабильность, активность) перед каждым взаимодействием
- Web-first утверждения повторяются до выполнения условия, устраняя состояния гонки
- Используйте
waitFor()иwaitForResponse()только когда нужно ждать асинхронные операции за пределами простых взаимодействий - Никогда не используйте мгновенные утверждения для динамического контента — всегда используйте
expect()из@playwright/test