⚖️ Старый vs Новый подход
⚡ Кратко
- Alert: Старое —
time.sleep→switch_to.alert. Новое —wait.until(EC.alert_is_present()) - Вкладки: Старое — угадывать индекс из
window_handles[1]. Новое — сравнение сcurrent_window_handle - ActionChains: Старое —
.click(element).perform()каждый раз. Новое — цепочка.move_to_element().click().perform() - iframe: Старое —
switch_to.frame(0)(ненадёжный индекс). Новое —switch_to.frame(WebElement)
1. Alert: ожидание появления
Старый подход (из лекции)
# test_alert_old.py
import time
# Жёсткое ожидание — антипаттерн
time.sleep(3)
alert = driver.switch_to.alert
alert.accept()
- Тест медленный: всегда ждёт 3 секунды
- Нестабильный: на медленной машине alert может ещё не появиться
- Нет обработки — если alert не появится, тест упадёт с непонятной ошибкой
Современный подход (Selenium 4)
# test_alert_new.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
# Ждём до 10 секунд, срабатывает сразу как alert появился
alert = wait.until(EC.alert_is_present())
alert.accept()
- Тест быстрый: срабатывает сразу после появления alert
- Надёжный: явный таймаут и понятное исключение (
TimeoutException) - Возвращает объект alert напрямую
2. Переключение на новую вкладку
Старый подход
# test_tabs_old.py
# Предположение, что новая вкладка — индекс 1
# Если вкладок больше или порядок изменился — падает
driver.switch_to.window(driver.window_handles[1])
- Хрупко: предполагаем фиксированный индекс
- Если открыто больше 2 вкладок — нужная может быть не [1]
Современный подход (Selenium 4)
# test_tabs_new.py
# Запоминаем текущую вкладку
original = driver.current_window_handle
# ... действие открывает новую вкладку ...
# Ищем именно новую вкладку
new_tab = [h for h in driver.window_handles if h != original][0]
driver.switch_to.window(new_tab)
- Работает независимо от количества вкладок
- Явно определяем «новую» вкладку через разницу множеств
- Безопасно при параллельном открытии нескольких вкладок
3. ActionChains: создание объекта
Старый подход
# test_actions_old.py
# Создаём новый ActionChains для каждого действия
ActionChains(driver).move_to_element(el1).perform()
ActionChains(driver).move_to_element(el2).perform()
ActionChains(driver).click(el3).perform()
- Три отдельных объекта — три отдельных действия
- Избыточно; нет накопления в цепочку
Современный подход (Selenium 4)
# test_actions_new.py
# Один объект, цепочка действий
actions = ActionChains(driver)
actions.move_to_element(el1)
actions.move_to_element(el2)
actions.click(el3)
actions.perform() # Выполняем всё одним вызовом
- Один объект переиспользуется
- Цепочка выполняется как единое действие — меньше гонок
- Удобнее читать и расширять
4. Работа с iframe: выбор фрейма
Старый подход
# test_iframe_old.py
# Переключение по индексу
driver.switch_to.frame(0)
# Переключение по имени/id как строка — без проверки существования
driver.switch_to.frame("myFrame")
- Индекс хрупок: изменится порядок фреймов — тест упадёт
- Нет ожидания: если iframe загружается медленно —
NoSuchFrameException
Современный подход (Selenium 4)
# test_iframe_new.py
from selenium.webdriver.support import expected_conditions as EC
# Ждём появления iframe и переключаемся по WebElement
wait = WebDriverWait(driver, 10)
iframe = wait.until(
EC.presence_of_element_located((By.TAG_NAME, "iframe"))
)
driver.switch_to.frame(iframe)
# ... работаем внутри ...
# Обязательно возвращаемся
driver.switch_to.default_content()
- WebElement устойчив к изменению порядка фреймов
- Ожидание через EC гарантирует, что iframe уже загружен
- Явный возврат через
default_content()
5. Загрузка файла: путь к файлу
Старый подход
# test_upload_old.py
# Жёстко заданный путь — не работает на другой машине
file_input.send_keys("C:\\Users\\user\\test.txt")
# Относительный путь — зависит от текущей директории запуска
file_input.send_keys("test.txt")
- Жёсткий путь: тест не работает у другого разработчика
- Относительный путь: зависит от того, откуда запущен pytest
Современный подход (Selenium 4)
# test_upload_new.py
import os
# os.path.abspath строит абсолютный путь от текущей директории
file_path = os.path.abspath("test_file.txt")
file_input.send_keys(file_path)
# Ещё надёжнее — путь относительно файла теста:
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "test_file.txt")
file_input.send_keys(file_path)
os.path.abspath()работает на любой ОС- Путь через
__file__не зависит от рабочей директории pytest - Не нужно экранировать слэши вручную