Когда вы пытаетесь протестировать основные пользовательские потоки сайта с помощью Playwright и сталкиваетесь с тем, что элемент не загружается, многие идут простым путем: ждут фиксированное количество времени, добавляя жестко закодированный таймаут. Но жесткие таймауты – это антипаттерн, потому как они снижают скорость, увеличивают вероятность поломки скрипта и часто приводят к нестабильности тестов.
Давайте разберемся, в чем суть проблемы, как она возникает, и как Playwright позволяет тестировать сайты, ориентируясь на пользователя в первую очередь.
Содержание:
- Проблема жестко прописанных ожиданий и инструментов без автоожидания
- Положитесь на автоожидание Playwright
- Другие механизмы ожиданий
Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ
Проблема жесткого ожидания и инструментов без автоожидания
Жесткие ожидания и таймауты выполняют только одну задачу: они заставляют Playwright ждать указанное время. Это делает их опасными: они достаточно интуитивны, чтобы им отдавали предпочтение новички-тестировщики, и достаточно негибки, чтобы создавать им серьезные проблемы. Давайте рассмотрим на практике, как это происходит.
Представьте себе ситуацию, в которой ваш сценарий использует инструмент без интеллектуального автоматического ожидания: тогда вам нужно дождаться появления элемента на странице, чтобы взаимодействовать с ним. Чтобы нажать на кнопку, необходимо убедиться, что она доступна в текущей сессии браузера. В качестве быстрого решения можно рассмотреть возможность добавления жесткого таймаута. Это может выглядеть примерно так:
// инструмент без встроенного автоожидания await page.waitFor(1000) // hard wait for 1000ms await page.click('#button-login')
В такой ситуации может произойти следующее:
- В итоге вы будете ждать меньше времени, чем требуется элементу для загрузки!

Жесткое ожидание завершается, и действие щелчка выполняется слишком рано. Сценарий завершается с ошибкой, возможно типа “Элемент не найден “.
- Элемент загружается до истечения срока ожидания.

Хотя элемент правильно нажимается по истечении ожидания и ваш скрипт продолжает выполняться по плану, вы теряете время. Вы никогда не нажмете на элемент, как только он станет доступен, но всегда будете полагаться на какое-то магическое число, чтобы взаимодействовать с интерфейсом. Это потерянное время быстро увеличивается, когда ваш тестовый набор растет или вы запускаете сразу несколько тестовых наборов. Если вы когда-нибудь сталкивались с ожиданием выполнения тестового набора, вы знаете, насколько это мучительно. При автоматизации браузера и сквозном тестировании важна каждая секунда!
Жесткие ожидания и таймауты всегда либо слишком короткие, либо слишком длинные. А в худшем случае из-за колебаний времени загрузки ожидание может быть то слишком долгим, то слишком коротким, что приводит к случайным сбоям в выполнении скрипта. Вы должны избегать такой ситуации любой ценой, потому что это приведет к непредсказуемым псевдослучайным сбоям, известным как “хлопчатость” тестов.
Хлопчатость, или нестабильность выше допустимой, и непредсказуемая частота сбоев являются серьезными проблемами. Это источник “шума”, затрудняющий понимание состояния системы, которую вы пытаетесь тестировать или контролировать. Если в вашем наборе сквозных тестов высок процент нестабильных тестов, то стейкхолдеры, которым регулярно приходится исследовать эти сбои, быстро потеряют доверие к вашей системе автоматизации.
Ваши тесты и сценарии автоматизации должны быть стабильными, а в сценариях сквозного тестирования и мониторинга никогда не должно быть жестких ожиданий.
Избегайте длительных ожиданий, полагаясь на автоожидание Playwright
Чтобы избежать этих проблем, вам следует забыть о существовании жестких ожиданий и использовать инструменты типа Playwright с механизмами автоматического ожидания. Теперь вернемся к нашему примеру и воспользуемся функциональностью Playwright.
// Playwright с встроенным автоожиданием // `click()` ожидает появления и стабилизации элемента, ... await page.getByRole('button', { name: 'Login' }).click()
Вы наверное заметили, что в приведенном выше сценарии больше нет оператора wait
. Жесткие ожидания не нужны в сценариях Playwright, поскольку при вызове действия в Playwright, такого как click
, fill
или selectOption
, Playwright автоматически ожидает, пока выполнится набор проверок на возможность выполнения действия.
Доступность элемента в Playwright
Чтобы убедиться в правильности действий по автоматизации, Playwright не полагается на тайминги. Ваша задача – определить действия браузера и ожидаемые результаты в интерфейсе; все остальное Playwright сделает сам.
Если вы хотите щелкнуть по элементу, Playwright будет взаимодействовать с ним только тогда, когда элемент готов и готов к действию. Готовность к действию оценивается следующими проверками:
- Локатор находит ровно один элемент?
- Виден ли этот элемент?
- Стабилен ли этот элемент? (он не движется, не анимируется и не изменяется)
- Может ли элемент принимать события? (он не закрыт и не заслонен другими элементами)
- Включен ли элемент? (у него нет атрибута
disabled
).
Когда вы поручаете Playwright выполнить действие, он будет постоянно проверять, соответствует ли элемент локатору и готов ли он к взаимодействию. И только при наличии элемента, готового к взаимодействию, он выполнит заданное действие.

Этот подход с автоожиданием имеет два основных преимущества:
- Сценарии в Playwright будут работать максимально быстро, потому что Playwright будет взаимодействовать с элементами, только когда они будут готовы.
- Вы можете сосредоточиться на описании действий пользовательского интерфейса и ожидаемых результатов, а не думать о таймаутах и таймингах сети.
Другие механизмы ожидания
Как правило, рекомендуется полагаться на автоожидание Playwright и встроенные утверждения web-first, но если вам необходимо, вот некоторые другие механизмы ожидания.
Ожидание навигации и состояния сети в Playwright
Если вы не можете ждать, пока элемент появится на странице, и хотите явно дождаться появления сети, используйте следующее.
page.waitForLoadState
ожидает, пока не будет достигнуто требуемое состояние загрузки. По умолчанию используется событие load
страницы, но также может быть настроено на ожидание domcontentloaded
или networkidle
(не рекомендуется).
// ожидание события `load` await page.waitForLoadState();
page.waitForURL
ожидает перехода на целевой URL. Также по умолчанию используется событие загрузки
страницы, но может быть настроено на ожидание commit
, domcontentloaded
или networkidle
(не рекомендуется).
// ожидание события `load` соответствующего `/login` URL await page.waitForURL('**/login');
Вы также можете дождаться отправки запроса или получения ответа с помощью page.waitForRequest
и page.waitForResponse
. Эти два метода являются ключевыми для реализации перехвата запросов и ответов.
// ожидание выполнения запроса после нажатия кнопки const loginRequestPromise = page.waitForRequest('/login') await page.getByRole('button', { name: 'Login' }).click() const loginRequest = await loginRequestPromise // ожидание ответа после нажатия кнопки const loginResponsePromise = page.waitForResponse('/login') await page.getByRole('button', { name: 'Login' }).click() const loginResponse = await loginResponsePromise
Ожидание элемента в Playwright
Существует множество способов дождаться появления элемента на странице. Чтобы дождаться, когда элемент станет видимым или достигнет определенного состояния, рекомендуется использовать утверждения Playwright типа web-first. toBeVisible
, toBeEnabled
, toBeChecked
и многие другие утверждения являются асинхронными – они ждут, пока элементы изменятся, появятся или исчезнут.
// ожидание пока кнопка не будет видна, стабильная, ,,, и нажать ее await page.getByRole('button', { name: 'Login' }).click() // ожидание пока кнопка не будет отключена await expect(page.getByRole('button', {name: 'Login'})).toBeDisabled() // ожидание пока кнопка не спрячется await expect(page.getByRole('button', {name: 'Login'})).toBeHidden()
Если вы объедините утверждения web-first с автоматическим ожиданием Playwright, ваши скрипты будут выразительными, понятными и, что самое главное, не будут содержать жестких таймаутов.
Если вы не используете Playwright Test (утверждения web-first доступны только в @playwright/test
) и хотите дождаться, пока элемент станет видимым, используйте locator.waitFor
.
const button = page.getByRole('button', { name: 'Login' }).click() // ожидание видимости кнопки await button.waitFor()
В любом случае, хорошее знание локаторов – это ключ к тому, чтобы вы могли выбрать именно тот элемент, который нам нужно дождаться.
Ожидание событий на странице
С помощью Playwright вы также можете напрямую ждать событий страницы, таких как popup
или download
, используя page.waitForEvent
.
// ожидание открытия нового окна или попапа после нажатия кнопки const popupPromise = page.waitForEvent('popup') await page.getByRole('button', { name: 'Open new window' }).click() const popup = await popupPromise;
Ожидание функций страницы
А для более сложных случаев вы можете передать функцию, которая будет проверяться в контексте браузера, через page.waitForFunction
.
// ожидание нужного состояния в окне браузера после нажатия кнопки const secretInternalState = page.waitForFunction( () => window.localstorage.secretThing === 'true' ); await page.getByRole('button', { name: 'Login' }).click() await secretInternalState;
Главное
- Никогда не используйте жесткие ожидания и таймауты.
- Вместо этого используйте автоожидания.
- Объедините действия автоожидания с утверждениями web-first, чтобы тестировать состояние UI, а не детали реализации.
Перевод статьи «Dealing with waits and timeouts in Playwright».