📖 Теория — Расширенные практики Selenium
⚡ Кратко
Selenium поддерживает три вида ожидания. Явное (WebDriverWait + EC) — предпочтительно. Неявное (implicitly_wait) — глобально, нет контроля условий. Жёсткое (time.sleep) — антипаттерн. AJAX-страницы требуют явного ожидания конкретного состояния.
Ожидания в Selenium
При автоматизации тестирования важно корректно обрабатывать ожидания загрузки элементов. Без этого скрипт будет работать нестабильно: иногда проходить, иногда падать из-за несвоевременной загрузки элементов.
В Selenium есть три метода ожидания элементов на странице:
- Неявные ожидания (
Implicit Wait) - Явные ожидания (
Explicit Wait) - Жёсткие ожидания (
time.sleep) — не рекомендуется
Неявные ожидания (Implicit Wait)
Это глобальная задержка, которая применяется ко всем методам find_element и find_elements. Если элемент не найден сразу, Selenium будет ждать заданное время перед тем, как выбросить исключение.
Пример использования
# conftest.py или test_example.py
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.implicitly_wait(10) # Ожидание до 10 секунд для каждого поиска элемента
driver.get("https://example.com")
element = driver.find_element(By.ID, "some_id") # Selenium будет ждать появления до 10 секунд
Плюсы:
- Простая настройка — одна строка для всех поисков
- Подходит, если сайт загружается примерно одинаково
Минусы:
- Применяется ко всем элементам, даже если ждать не требуется
- Не позволяет ждать специфические условия (например, изменение текста элемента)
- При смешивании с явными ожиданиями поведение становится непредсказуемым
Явные ожидания (Explicit Wait) — рекомендуемый подход
Позволяют ожидать появления конкретного элемента или его состояния (например, visible, clickable, contains text). Используются с WebDriverWait.
Пример использования
# test_explicit.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
wait = WebDriverWait(driver, 10) # Ожидание до 10 секунд
# Ждём, пока элемент появится в DOM:
element = wait.until(EC.presence_of_element_located((By.ID, "some_id")))
# Или ждём, пока элемент станет кликабельным:
clickable_element = wait.until(EC.element_to_be_clickable((By.ID, "button_id")))
clickable_element.click()
Плюсы:
- Можно ожидать конкретное состояние элемента
- Гибкость — можно использовать разные условия ожидания
- Быстрее неявных: не ждёт максимальное время, если условие выполнилось раньше
Минусы:
- Требует больше кода
- Нужно вручную указывать условия для каждого ожидания
Жёсткие ожидания (time.sleep) — антипаттерн
Метод time.sleep() останавливает выполнение кода на фиксированное время, независимо от того, загрузился ли элемент.
# ПЛОХОЙ ПРИМЕР - так делать не нужно
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
time.sleep(5) # Ожидание 5 секунд, даже если элемент появился раньше
element = driver.find_element(By.ID, "some_id")
Минусы time.sleep():
- Неэффективен — если страница загружается быстрее, время тратится впустую
- Делает тесты медленнее — если ждать дольше, чем требуется
- Ненадёжен — если страница загружается дольше ожидания, тест упадёт
Сравнение методов ожидания
| Метод | Применение | Плюсы | Минусы |
|---|---|---|---|
implicitly_wait |
Глобальное ожидание элементов | Упрощает код, минимальные настройки | Не подходит для специфических состояний |
WebDriverWait + EC |
Ожидание конкретных условий | Гибкость, надёжность, скорость | Требует больше кода |
time.sleep |
Жёсткое ожидание | Прост в использовании | Медленный, ненадёжный, антипаттерн |
Вывод: лучший подход — явные ожидания (WebDriverWait + EC), так как они точечно решают проблему ожидания и делают тесты надёжными и быстрыми.
Expected Conditions (EC)
Это предопределённые условия, используемые с WebDriverWait, чтобы ожидать выполнения определённых событий на веб-странице.
presence_of_element_located
Ожидает, пока элемент появится в DOM, но не обязательно станет видимым пользователю.
# Использовать, когда нужно только наличие в DOM (элемент может быть скрыт)
wait.until(EC.presence_of_element_located((By.ID, "element_id")))
visibility_of_element_located
Ожидает, пока элемент появится в DOM и станет видимым.
# Использовать, когда элемент должен быть виден пользователю
wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "visible-element")))
element_to_be_clickable
Ожидает, пока элемент станет кликабельным (видим и включён).
# Использовать перед click() — гарантирует, что элемент готов к нажатию
button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[text()='Submit']")))
button.click()
text_to_be_present_in_element
Ожидает, пока определённый текст появится внутри элемента.
# Использовать для проверки динамически загружаемого текста (AJAX-ответы)
wait.until(EC.text_to_be_present_in_element((By.TAG_NAME, "h1"), "Dashboard"))
url_contains
Ожидает, пока URL изменится и будет содержать определённую подстроку.
# Использовать для проверки навигации/редиректа
wait.until(EC.url_contains("dashboard"))
title_contains
Ожидает, пока заголовок страницы (title) будет содержать определённый текст.
# Использовать для проверки загрузки нужной страницы
wait.until(EC.title_contains("Dashboard"))
frame_to_be_available_and_switch_to_it
Ожидает, пока iframe станет доступен, затем переключается на него.
# Использовать, если тест взаимодействует с iframe
wait.until(EC.frame_to_be_available_and_switch_to_it((By.TAG_NAME, "iframe")))
alert_is_present
Ожидает появления JavaScript-уведомления (alert).
# Использовать для обработки всплывающих окон
alert = wait.until(EC.alert_is_present())
alert.accept() # Закрыть alert
staleness_of
Ожидает, пока элемент будет удалён из DOM (устареет).
# Использовать для проверки, что элемент исчез после действия
old_element = driver.find_element(By.ID, "old-element")
wait.until(EC.staleness_of(old_element))
element_located_to_be_selected
Ожидает, пока чекбокс или radio-button будет выбран.
# Использовать при тестировании форм с чекбоксами
wait.until(EC.element_located_to_be_selected((By.NAME, "checkbox")))
Итоги: когда использовать каждое условие?
| Expected Condition | Когда использовать? |
|---|---|
presence_of_element_located | Элемент присутствует в DOM, но может быть скрыт |
visibility_of_element_located | Элемент должен быть видимым |
element_to_be_clickable | Нужно дождаться, пока элемент станет активным для клика |
text_to_be_present_in_element | Нужно дождаться появления определённого текста |
url_contains | Нужно убедиться, что произошёл переход на нужный URL |
title_contains | Заголовок страницы должен содержать нужный текст |
frame_to_be_available_and_switch_to_it | Нужно дождаться и переключиться в iframe |
alert_is_present | Появится всплывающее JavaScript-уведомление |
staleness_of | Элемент должен исчезнуть из DOM |
element_located_to_be_selected | Чекбокс или radio-button должен быть выбран |
Что такое AJAX?
AJAX (Asynchronous JavaScript and XML) — технология, позволяющая загружать данные с сервера без перезагрузки страницы. Она особенно полезна для динамических веб-приложений:
- Загрузка комментариев
- Обновление данных в реальном времени
- Автодополнение полей поиска
При тестировании AJAX-страниц time.sleep и implicitly_wait недостаточно надёжны — нужно ждать конкретного состояния элемента (появления текста, видимости, кликабельности) с помощью явных ожиданий.
Взаимодействие с элементами
Базовые методы взаимодействия с найденными элементами:
send_keys() — ввод текста
# test_interaction.py
from selenium.webdriver.common.by import By
# Находим поле ввода и вводим текст
username_field = driver.find_element(By.NAME, "username")
username_field.send_keys("Admin")
clear() — очистка поля
# Очищаем поле перед вводом нового значения (если в поле уже есть текст)
delay_input = driver.find_element(By.CSS_SELECTOR, "#delay")
delay_input.clear()
delay_input.send_keys("45")
click() — нажатие на элемент
# Нажимаем на кнопку; лучше дождаться кликабельности через EC
button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit']")))
button.click()
Рекомендация: перед click() используйте EC.element_to_be_clickable, а не просто find_element. Это гарантирует, что кнопка не только найдена в DOM, но и готова к нажатию.