📖 Теория — Расширенные практики Selenium

← К оглавлению урока

⚡ Кратко

Selenium поддерживает три вида ожидания. Явное (WebDriverWait + EC) — предпочтительно. Неявное (implicitly_wait) — глобально, нет контроля условий. Жёсткое (time.sleep) — антипаттерн. AJAX-страницы требуют явного ожидания конкретного состояния.

Ожидания в Selenium

При автоматизации тестирования важно корректно обрабатывать ожидания загрузки элементов. Без этого скрипт будет работать нестабильно: иногда проходить, иногда падать из-за несвоевременной загрузки элементов.

В Selenium есть три метода ожидания элементов на странице:

  1. Неявные ожидания (Implicit Wait)
  2. Явные ожидания (Explicit Wait)
  3. Жёсткие ожидания (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, но и готова к нажатию.