Selenium и Python: создание стабильных автотестов от А до Я
Для кого эта статья:
- Новички в области автоматизации тестирования, желающие освоить Selenium и Python.
- Специалисты и практикующие QA-инженеры, интересующиеся улучшением своих навыков и инструментов.
Студенты, обучающиеся на курсах или самостоятельно изучающие тестирование программного обеспечения.
Автоматизированное тестирование — мощный инструмент для тех, кто ценит время и качество. Selenium с Python стал золотым стандартом в этой области благодаря простоте синтаксиса и широким возможностям. Перед вами подробное руководство для всех, кто хочет научиться писать надежные тесты, избегая хрупких решений и бесконечных правок. Освоив эти техники, вы сможете автоматизировать однообразные проверки и сосредоточиться на действительно сложных задачах тестирования. 🚀
Хотите стать профессиональным тестировщиком и получать достойную оплату за свои навыки? Курс тестировщика ПО от Skypro разработан под реальные требования работодателей. За 9 месяцев вы освоите не только Selenium с Python, но и полный стек инструментов QA-инженера. Программа включает работу с реальными проектами, менторство от практикующих специалистов и гарантию трудоустройства. Инвестируйте в навыки, которые точно окупятся!
Настройка Selenium в Python для автоматизации тестов
Прежде чем писать первую строчку кода для автоматизации, необходимо правильно настроить рабочее окружение. Этот этап часто вызывает затруднения у новичков, но следуя четкому алгоритму, вы избежите большинства проблем.
Начнем с установки необходимых компонентов:
- Установите Python (рекомендуется версия 3.8 и выше)
- Установите Selenium WebDriver через pip:
pip install selenium - Загрузите драйвер для вашего браузера (ChromeDriver, GeckoDriver для Firefox и т.д.)
- Добавьте путь к драйверу в системные переменные PATH
Для тех, кто предпочитает автоматизированный подход, существует удобный способ установки веб-драйверов с помощью WebDriver Manager:
pip install webdriver-manager
Это избавит вас от необходимости вручную загружать и обновлять драйверы. В коде это выглядит так:
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(ChromeDriverManager().install())
Проверьте корректность установки, написав простой скрипт:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.python.org')
print(driver.title)
driver.quit()
Если код выполнился без ошибок и вы увидели заголовок страницы Python.org в консоли — поздравляю, базовая настройка завершена! 🎉
| Браузер | Драйвер | Установка через webdriver-manager |
|---|---|---|
| Chrome | ChromeDriver | webdriver_manager.chrome.ChromeDriverManager().install() |
| Firefox | GeckoDriver | webdriver_manager.firefox.GeckoDriverManager().install() |
| Edge | EdgeDriver | webdriver_manager.microsoft.EdgeChromiumDriverManager().install() |
| Safari | Встроен в macOS | Не требуется (только включить Developer Tools) |
Настоятельно рекомендую создать виртуальное окружение для ваших тестов, чтобы избежать конфликтов с другими проектами:
python -m venv selenium_env
source selenium_env/bin/activate # для Linux/Mac
selenium_env\Scripts\activate # для Windows
Михаил Прокофьев, Lead QA Engineer
Когда я только начинал работать с Selenium, я потратил почти два дня на попытки заставить его корректно работать. Главная проблема была в несоответствии версий браузера и драйвера. После очередного обновления Chrome тесты просто перестали запускаться. Тогда я узнал про webdriver-manager, и это изменило всё. Теперь я использую его во всех проектах и рекомендую команде. Один раз настроив CI/CD с автоматическим обновлением драйверов, мы забыли о проблемах совместимости. Мой совет: не экономьте время на правильной настройке окружения — это сэкономит вам недели работы в будущем.

Первые шаги: поиск и взаимодействие с элементами
После успешной настройки окружения пора научиться находить элементы на веб-странице и взаимодействовать с ними. Это фундаментальные навыки для любого автоматизатора тестирования. 🔍
Selenium предлагает несколько способов поиска элементов:
- findelementby_id — поиск по атрибуту id (наиболее эффективный)
- findelementby_name — поиск по атрибуту name
- findelementby_xpath — поиск с помощью XPath выражений
- findelementbycssselector — поиск с помощью CSS-селекторов
- findelementbyclassname — поиск по имени класса
- findelementbytagname — поиск по имени тега
- findelementbylinktext — поиск ссылки по точному тексту
- findelementbypartiallink_text — поиск ссылки по части текста
Однако современный подход предполагает использование метода find_element с указанием локатора:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.python.org')
# Поиск элемента по ID
search_box = driver.find_element(By.ID, 'id-search-field')
# Поиск элемента по CSS-селектору
submit_button = driver.find_element(By.CSS_SELECTOR, 'button.search-button')
После нахождения элемента вы можете выполнять с ним различные действия:
# Ввод текста в поле
search_box.send_keys('selenium python')
# Клик по кнопке
submit_button.click()
# Очистка текстового поля
search_box.clear()
# Проверка отображения элемента
is_displayed = search_box.is_displayed()
# Получение атрибута элемента
placeholder = search_box.get_attribute('placeholder')
Иногда нужно найти несколько элементов сразу. Для этого используйте метод find_elements:
# Найти все ссылки на странице
all_links = driver.find_elements(By.TAG_NAME, 'a')
# Перебор найденных элементов
for link in all_links:
print(link.get_attribute('href'))
При работе с элементами часто требуется подождать их появления на странице. Selenium предлагает три типа ожиданий:
- Неявные ожидания (Implicit Waits) — глобальная настройка для всех элементов
- Явные ожидания (Explicit Waits) — ожидание конкретного условия для элемента
- Fluent ожидания (FluentWait) — гибкие ожидания с дополнительными параметрами
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Неявное ожидание
driver.implicitly_wait(10) # секунд
# Явное ожидание
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, 'results'))
)
| Локатор | Скорость | Стабильность | Когда использовать |
|---|---|---|---|
| ID | Высокая | Высокая | Первый выбор, если доступен |
| CSS | Высокая | Средняя | Когда нужна гибкость и нет ID |
| XPath | Низкая | Средняя | В сложных случаях или при динамической структуре |
| Имя класса | Средняя | Низкая | Только если класс уникален и стабилен |
| Текст ссылки | Средняя | Низкая | Для ссылок при отсутствии других стабильных атрибутов |
Навигация и управление браузером через Selenium
Управление браузером — это больше, чем просто поиск и взаимодействие с элементами. Selenium позволяет полностью контролировать поведение браузера: перемещаться по страницам, управлять окнами, работать с cookie и многое другое. 🌐
Основные действия по навигации:
# Открытие страницы
driver.get('https://www.python.org')
# Получение текущего URL
current_url = driver.current_url
# Получение заголовка страницы
title = driver.title
# Получение исходного кода страницы
page_source = driver.page_source
# Переход назад/вперед в истории браузера
driver.back()
driver.forward()
# Обновление страницы
driver.refresh()
При работе с многооконными приложениями или вкладками важно уметь переключаться между ними:
# Открытие новой вкладки
driver.execute_script("window.open('https://www.selenium.dev', '_blank')")
# Получение списка идентификаторов окон
window_handles = driver.window_handles
# Переключение на новую вкладку (обычно последнюю в списке)
driver.switch_to.window(window_handles[-1])
# Переключение обратно на первую вкладку
driver.switch_to.window(window_handles[0])
# Закрытие текущей вкладки
driver.close()
# Закрытие всех вкладок и завершение сеанса
driver.quit()
Для работы с всплывающими окнами (alerts) используйте следующие методы:
# Переключение на алерт
alert = driver.switch_to.alert
# Получение текста алерта
alert_text = alert.text
# Принятие алерта (нажатие OK)
alert.accept()
# Отклонение алерта (нажатие Cancel)
alert.dismiss()
# Ввод текста в алерт (если поддерживается)
alert.send_keys('Текст для ввода')
Часто требуется управлять cookies, особенно при тестировании авторизации:
# Добавление cookie
driver.add_cookie({"name": "session_id", "value": "12345"})
# Получение всех cookies
all_cookies = driver.get_cookies()
# Получение конкретной cookie
session_cookie = driver.get_cookie("session_id")
# Удаление cookie
driver.delete_cookie("session_id")
# Удаление всех cookies
driver.delete_all_cookies()
Для выполнения JavaScript на странице используйте метод execute_script:
# Выполнение простого скрипта
driver.execute_script("return document.title")
# Прокрутка до элемента
element = driver.find_element(By.ID, "my-element")
driver.execute_script("arguments[0].scrollIntoView(true);", element)
# Скрытие элемента
driver.execute_script("arguments[0].style.display='none';", element)
Для настройки размеров и позиции окна браузера:
# Установка размеров окна
driver.set_window_size(1920, 1080)
# Установка позиции окна
driver.set_window_position(0, 0)
# Разворачивание окна на весь экран
driver.maximize_window()
# Сворачивание окна
driver.minimize_window()
Анна Сергеева, Senior Test Automation Engineer
В одном из моих проектов мы столкнулись с проблемой: после авторизации пользователя в приложении нам нужно было протестировать функциональность, доступную только в новой вкладке. При каждом запуске теста система генерировала новую вкладку, но наши скрипты продолжали работать в исходной. Тесты стабильно падали. Мы перепробовали разные способы, пока не нашли решение: после действия, открывающего новую вкладку, мы стали получать список всех открытых окон через windowhandles и явно переключаться на нужную вкладку. Эта простая строчка кода спасла нам недели работы и превратила нестабильные тесты в надежные: `driver.switchto.window(driver.window_handles[-1])`. Теперь это стандартная практика в нашей тестовой базе, и новички в команде быстро учатся применять этот паттерн.
Создание комплексных тестов с Selenium и Python
Отдельные команды Selenium полезны для изучения, но в реальной работе вы будете создавать комплексные тесты, объединяющие множество действий. Давайте рассмотрим, как структурировать тесты, используя современные подходы и фреймворки. 🧩
Для организованного тестирования рекомендую использовать unittest или pytest. Рассмотрим пример с pytest, который более гибок и современен:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
@pytest.fixture
def browser():
driver = webdriver.Chrome()
driver.implicitly_wait(10)
yield driver
driver.quit()
def test_python_search(browser):
# Открываем страницу
browser.get("https://www.python.org")
# Находим поле поиска и вводим запрос
search_field = browser.find_element(By.ID, "id-search-field")
search_field.clear()
search_field.send_keys("pycon")
# Нажимаем кнопку поиска
browser.find_element(By.ID, "submit").click()
# Проверяем, что результаты поиска отображаются
WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "list-recent-events"))
)
# Проверяем, что "pycon" присутствует в результатах
results = browser.find_elements(By.CSS_SELECTOR, ".list-recent-events li")
assert len(results) > 0, "Результаты поиска не найдены"
# Проверяем текст в результатах
page_text = browser.page_source.lower()
assert "pycon" in page_text, "Запрос 'pycon' не найден в результатах"
Для более структурированного подхода рекомендуется использовать паттерн Page Object Model (POM), который отделяет логику тестов от их реализации:
# file: pages.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
def __init__(self, driver):
self.driver = driver
def find_element(self, locator, time=10):
return WebDriverWait(self.driver, time).until(
EC.presence_of_element_located(locator),
message=f"Can't find element by locator {locator}"
)
class MainPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.url = "https://www.python.org"
def open(self):
self.driver.get(self.url)
def search_for(self, query):
search_field = self.find_element((By.ID, "id-search-field"))
search_field.clear()
search_field.send_keys(query)
submit_button = self.find_element((By.ID, "submit"))
submit_button.click()
class SearchResultsPage(BasePage):
def get_results(self):
results_list = self.find_element((By.CLASS_NAME, "list-recent-events"))
items = results_list.find_elements(By.TAG_NAME, "li")
return items
def has_results(self):
return len(self.get_results()) > 0
def contains_text(self, text):
page_text = self.driver.page_source.lower()
return text.lower() in page_text
# file: test_search.py
import pytest
from selenium import webdriver
from pages import MainPage, SearchResultsPage
@pytest.fixture
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_python_search_with_pom(browser):
# Инициализация страниц
main_page = MainPage(browser)
results_page = SearchResultsPage(browser)
# Выполнение теста
main_page.open()
main_page.search_for("pycon")
# Проверки
assert results_page.has_results(), "Результаты поиска не найдены"
assert results_page.contains_text("pycon"), "Запрос 'pycon' не найден в результатах"
Для создания надежных тестов важно учитывать следующие принципы:
- Изоляция тестов — каждый тест должен быть независимым от других
- Идемпотентность — возможность многократного запуска без изменения результата
- Атомарность — тест должен проверять одну конкретную функциональность
- Явные ожидания — всегда используйте их вместо time.sleep()
- Корректная очистка — всегда освобождайте ресурсы (driver.quit(), а не driver.close())
Для параметризации тестов и запуска с разными данными используйте возможности pytest:
import pytest
@pytest.mark.parametrize("query,expected_text", [
("pycon", "pycon"),
("django", "web framework"),
("flask", "micro framework")
])
def test_python_search_parametrized(browser, query, expected_text):
main_page = MainPage(browser)
results_page = SearchResultsPage(browser)
main_page.open()
main_page.search_for(query)
assert results_page.has_results(), f"Результаты для запроса '{query}' не найдены"
assert results_page.contains_text(expected_text), f"Текст '{expected_text}' не найден в результатах"
Полезные приемы и решение проблем в Selenium Python
Даже опытные автоматизаторы регулярно сталкиваются с трудностями при работе с Selenium. Рассмотрим типичные проблемы и проверенные способы их решения. 🔧
1. Создание скриншотов при падении теста
Это незаменимый инструмент диагностики проблем, особенно в CI/CD окружении:
def test_function(browser):
try:
# Тестовая логика
browser.get("https://www.example.com")
# ...
except Exception as e:
# Создание скриншота с временной меткой
import time
timestamp = int(time.time())
screenshot_path = f"error_{timestamp}.png"
browser.save_screenshot(screenshot_path)
print(f"Screenshot saved at {screenshot_path}")
raise # Передаем исключение дальше
2. Обход CAPTCHA в тестовом окружении
Для тестовых сред обычно можно договориться о специальном режиме:
- Используйте тестовые учетные записи с отключенной CAPTCHA
- Добавьте специальный флаг в URL или cookie для отключения CAPTCHA
- Для Google reCAPTCHA можно использовать ключ для тестирования:
6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
3. Работа с динамическими элементами
Один из самых сложных аспектов автоматизации — это взаимодействие с динамическими элементами:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import StaleElementReferenceException
# Переопределяем функцию для повторных попыток при устаревшей ссылке на элемент
def click_with_retry(driver, locator, max_attempts=3):
for attempt in range(max_attempts):
try:
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable(locator)
)
element.click()
return True
except StaleElementReferenceException:
if attempt == max_attempts – 1:
raise
print(f"StaleElementReferenceException, retrying... (attempt {attempt+1}/{max_attempts})")
# Использование
click_with_retry(driver, (By.ID, "dynamic-button"))
4. Работа с загрузкой файлов
Для автоматизации загрузки файлов используйте метод send_keys с абсолютным путем к файлу:
import os
# Получаем абсолютный путь к файлу
file_path = os.path.abspath("path/to/file.txt")
# Находим элемент загрузки и передаем путь
file_input = driver.find_element(By.ID, "fileUpload")
file_input.send_keys(file_path)
5. Обработка всплывающих окон и модальных диалогов
Существует два типа всплывающих элементов — JavaScript-алерты и HTML-модальные окна:
# Для JavaScript-алертов
try:
WebDriverWait(driver, 5).until(EC.alert_is_present())
alert = driver.switch_to.alert
alert.accept() # или alert.dismiss()
except:
print("No alert present")
# Для HTML-модальных окон
modal = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "modal-dialog"))
)
close_button = modal.find_element(By.CLASS_NAME, "close-btn")
close_button.click()
| Проблема | Симптом | Решение |
|---|---|---|
| ElementNotInteractableException | Элемент найден, но не доступен для клика | Использовать WebDriverWait с EC.elementtobe_clickable или JavaScript-клик |
| StaleElementReferenceException | Ссылка на элемент устарела после обновления DOM | Повторно находить элемент перед каждым взаимодействием |
| TimeoutException | Элемент не появился в течение заданного времени | Увеличить таймаут, проверить локатор, проверить видимость элемента |
| NoSuchElementException | Элемент не найден на странице | Проверить локатор, добавить ожидания, проверить фрейм |
| WebDriverException | Соединение с браузером потеряно | Проверить версии драйвера и браузера, использовать webdriver-manager |
6. Эффективное отладка тестов
Для диагностики проблемных тестов:
- Используйте явный вывод информации:
print(f"Current URL: {driver.current_url}") - Создавайте скриншоты в ключевых точках:
driver.save_screenshot("step_3.png") - Логируйте HTML-код проблемных элементов:
print(element.get_attribute('outerHTML')) - Используйте Python debugger (pdb) для пошагового выполнения
7. Управление окнами и фреймами
При работе с iframe необходимо переключаться между ними:
# Переключение на iframe по индексу
driver.switch_to.frame(0)
# Переключение на iframe по ID или имени
driver.switch_to.frame("iframe-id")
# Переключение на iframe по элементу
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)
# Возврат к основному содержимому
driver.switch_to.default_content()
8. Эмуляция различных устройств и разрешений экрана
Для тестирования отзывчивого дизайна:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
# Эмуляция мобильного устройства
mobile_emulation = {
"deviceName": "iPhone X"
}
chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Chrome(options=chrome_options)
# Или просто установка определенного разрешения
driver.set_window_size(375, 812) # iPhone X размеры
Автоматизированное тестирование с Selenium и Python — это мощный инструмент в руках тестировщика, позволяющий освободить время от рутинных проверок. Изучив основы и освоив практические приемы из этой статьи, вы сможете создавать надежные и эффективные тесты для ваших проектов. Помните главное правило автоматизации: тест должен быть стабильнее, чем код, который он проверяет. Инвестируйте время в хорошую архитектуру, грамотное управление ожиданиями и обработку исключений — это окупится многократно при поддержке и расширении вашей тестовой базы.