Python: Selenium

Теория: Действия на странице

Ввод текста и очистка полей

После того как Selenium открыл страницу и нашёл элемент, с ним можно взаимодействовать. Поля ввода - частый пример. Через них тестировщик имитирует ввод логина, пароля, комментария или любого другого текста.

Для этого используется метод send_keys(). Он передаёт символы в элемент точно так же, как если бы пользователь печатал их с клавиатуры.

Пример: ввод текста в поле поиска Google.

from selenium import webdriver
from selenium.webdriver.common.by import By


driver = webdriver.Chrome()
driver.get("https://www.google.com")

## Находим поле поиска по атрибуту name
search_box = driver.find_element(By.NAME, "q")

## Вводим запрос
search_box.send_keys("Selenium WebDriver")

## Отправляем форму нажатием Enter
search_box.submit()

driver.quit()

Selenium вводит символы один за другим и запускает поиск, как при ручном вводе.

Если перед вводом нужно очистить поле, используется метод clear(). Он удаляет всё содержимое элемента. Например, если нужно ввести новое значение:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com/login")

username = driver.find_element(By.NAME, "username")
password = driver.find_element(By.NAME, "password")

## Вводим текст
username.send_keys("user123")
password.send_keys("12345")

## Очищаем поле и вводим заново
username.clear()
username.send_keys("new_user")

driver.quit()

Методы send_keys() и clear() работают с любыми элементами ввода — <input>, <textarea> и даже с полями, созданными через JavaScript. При тестировании форм эти команды применяются почти в каждом сценарии: логин, регистрация, поиск, отправка комментариев.

Когда clear() не работает

В интерфейсах на React (в том числе Material UI) ввод часто контролируется JavaScript-состоянием. Метод clear() очищает значение в DOM, но компонент не получает событие изменения и мгновенно возвращает старое значение. В таких случаях поле очищают клавиатурой: выделить всё → удалить → ввести новое значение.

from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

title = WebDriverWait(driver, 5).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, 'input[name="title"]'))
)

title.send_keys(Keys.CONTROL, "a")   # на macOS вместо CONTROL используют Keys.COMMAND
title.send_keys(Keys.DELETE)
title.send_keys("Новый заголовок")

Такой приём стабильно срабатывает в Kanban-досках и других MUI-приложениях, где напрямую изменить value не удаётся.

Выпадающие списки MUI и role="combobox"

Компоненты Select в MUI скрывают настоящий <input> и рисуют кликабельный div с role="combobox". Если кликнуть в сам input, ничего не произойдёт, поэтому сначала нужно найти «триггер», а уже затем дождаться и выбрать пункт списка:

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

def select_from_dropdown(driver, selector: str, item_text: str):
    wait = WebDriverWait(driver, 10)
    element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))

    trigger = element
    if element.tag_name.lower() == "input":
        try:
            trigger = element.find_element(
                By.XPATH,
                "./parent::*//*[@role='combobox']",
            )
        except NoSuchElementException:
            trigger = element

    wait.until(lambda d: trigger.is_displayed() and trigger.is_enabled())
    try:
        trigger.click()
    except Exception:
        driver.execute_script("arguments[0].click();", trigger)

    option = wait.until(
        EC.element_to_be_clickable((By.XPATH, f"//li[normalize-space()='{item_text}']"))
    )
    option.click()

Ключевые моменты: дождаться появления списка, переключиться на role="combobox", использовать element_to_be_clickable для пункта и не полагаться на implicitly_wait.

Скрытые input в React Admin

Некоторые формы рендерят видимый блок и отдельный скрытый input[type="hidden"], который хранит выбранное значение. Чтобы обновить поле, приходится менять значение через JavaScript и руками отправлять событие:

from selenium.webdriver.common.by import By

container = driver.find_element(By.CSS_SELECTOR, "div[data-testid='select-status']")
hidden_input = driver.execute_script(
    "return arguments[0].querySelector('input[type=\"hidden\"]')",
    container,
)

driver.execute_script(
    """
    const [el, value] = arguments;
    el.value = value;
    el.dispatchEvent(new Event('input', { bubbles: true }));
    el.dispatchEvent(new Event('change', { bubbles: true }));
    """,
    hidden_input,
    "done",
)

После обновления значения стоит кликнуть по триггеру или подтвердить форму, чтобы фронтенд синхронизировал состояние. Этот подход полезен для React Admin и других библиотек с контролируемыми полями и скрытыми инпутами.

Получение текста и атрибутов элементов

После того как Selenium нашёл элемент, часто нужно проверить, что в нём отображается правильный текст или что у него есть нужные атрибуты — например, класс, значение или ссылка. Для этого используются свойства .text и метод .get_attribute().

Свойство .text возвращает видимый текст внутри элемента. Например, проверка заголовка страницы:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com")

## Находим заголовок страницы
title = driver.find_element(By.TAG_NAME, "h1")

## Получаем и выводим текст
print(title.text)  # Example Domain

driver.quit()

Selenium извлекает именно тот текст, который виден пользователю в браузере, без скрытых тегов и скриптов.

Если нужно получить не текст, а значение атрибута — используется метод .get_attribute(). Например, у ссылок часто проверяют атрибут href:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com")

## Находим ссылку на странице
link = driver.find_element(By.TAG_NAME, "a")

## Извлекаем значение атрибута href
url = link.get_attribute("href")

print(url)  # https://www.iana.org/domains/example

driver.quit()

С помощью .get_attribute() можно получить любое свойство HTML-элемента:

  • value — текущее значение поля ввода,
  • class — список CSS-классов,
  • id, name, title и другие.

Пример с полем ввода:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.w3schools.com/html/html_forms.asp")

field = driver.find_element(By.ID, "fname")
field.send_keys("Alex")

## Получаем введённый текст
print(field.get_attribute("value"))  # Alex

driver.quit()

Эти методы — основа проверки содержимого страницы. .text показывает, что видит пользователь, а .get_attribute() — что записано в коде элемента.

Тест авторизации

Тест авторизации проверяет, что пользователь с валидными данными попадает в личный кабинет, а с неверными — видит сообщение об ошибке. Для предсказуемого примера используется учебная страница https://the-internet.herokuapp.com/login, где логин tomsmith и пароль SuperSecretPassword!.

Ниже приведён вариант на Pytest, который опирается на фикстуру browser из conftest.py. В тесте применяются явные ожидания, чтобы дождаться элементов перед действиями и исключить «гонки» с загрузкой страницы.

## tests/test_auth.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

LOGIN_URL = "https://the-internet.herokuapp.com/login"
SECURE_URL = "https://the-internet.herokuapp.com/secure"

def test_auth_success(browser):
    ## Открытие страницы логина
    browser.get(LOGIN_URL)

    ## Поиск полей и ввод валидных данных
    WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, "username"))).send_keys("tomsmith")
    WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, "password"))).send_keys("SuperSecretPassword!")

    ## Нажатие на кнопку Login
    WebDriverWait(browser, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.radius"))).click()

    ## Проверка: редирект в защищённый раздел и наличие флеш-сообщения об успехе
    WebDriverWait(browser, 5).until(EC.url_contains("/secure"))
    flash = WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, "flash")))
    assert SECURE_URL in browser.current_url
    assert "You logged into a secure area!" in flash.text

def test_auth_invalid_password(browser):
    ## Открытие страницы логина
    browser.get(LOGIN_URL)

    ## Ввод валидного логина и неверного пароля
    WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, "username"))).send_keys("tomsmith")
    WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, "password"))).send_keys("wrong")

    ## Нажатие на кнопку Login
    WebDriverWait(browser, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.radius"))).click()

    ## Проверка: остались на странице логина и получено сообщение об ошибке
    WebDriverWait(browser, 5).until(EC.url_contains("/login"))
    flash = WebDriverWait(browser, 5).until(EC.visibility_of_element_located((By.ID, "flash")))
    assert LOGIN_URL in browser.current_url
    assert "Your password is invalid!" in flash.text

Поле username и password ищутся по id, кнопка входа — по CSS-селектору button.radius, флеш-сообщение — по id="flash". Для устойчивости применяются условия visibility_of_element_located, element_to_be_clickable и url_contains, чтобы действия выполнялись только после готовности страницы.

Завершение сессии браузера

После выполнения теста браузер обязательно закрывается. Это предотвращает зависание процессов WebDriver и освобождает ресурсы системы. Завершение выполняется методом quit(). Он закрывает все вкладки, завершает сессию драйвера и разрывает соединение между Selenium и браузером.

Пример минимального сценария:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://example.com")

## Завершение работы
driver.quit()

Иногда в коде встречается метод close(). Он закрывает только текущее окно браузера, но оставляет сессию активной. Если открыто несколько вкладок, остальные остаются работать. В автотестах почти всегда используется именно quit(), потому что он завершает всю сессию целиком.

Для Pytest правильнее управлять закрытием браузера через фикстуры. Тогда WebDriver будет автоматически завершать работу после каждого теста.

import pytest
from selenium import webdriver

@pytest.fixture
def browser():
    driver = webdriver.Chrome()
    yield driver
    driver.quit()

В этом примере код после yield выполняется всегда — даже если тест упал с ошибкой. Такой подход гарантирует, что браузер не останется висеть в памяти, а каждый тест начнёт работу с чистой сессией.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845