🐛 Типичные ошибки с локаторами

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

⚡ Топ-5 ошибок с локаторами

  • NoSuchElementException — элемент не найден: неверный локатор или элемент ещё не загрузился
  • CLASS_NAME с пробеломBy.CLASS_NAME, "btn btn-primary" падает; используйте CSS: .btn.btn-primary
  • Хрупкий XPath из DevTools — абсолютный путь сломается при любом изменении HTML
  • find_element vs find_elements — путаница типов; у find_elements нет .text напрямую
  • is_displayed() не означает "элемент существует" — сначала найдите, потом проверяйте видимость

Ошибка 1: NoSuchElementException

Симптом: тест падает с selenium.common.exceptions.NoSuchElementException: Message: no such element.

Причины:

  • Опечатка в локаторе (неверный ID, класс, XPath)
  • Элемент ещё не загрузился (нужны ожидания)
  • Элемент находится внутри iframe (нужен switch_to.frame)
  • Страница не та (неверный URL или редирект)
# ПРОБЛЕМА
element = driver.find_element(By.ID, "loginn")  # опечатка
# NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"[id='loginn']"}

# РЕШЕНИЕ: проверить ID через DevTools
element = driver.find_element(By.ID, "login")  # правильно
# ПРОБЛЕМА: элемент ещё не загрузился
driver.get("https://example.com")
element = driver.find_element(By.ID, "dynamic-content")  # ошибка!

# РЕШЕНИЕ (предварительное): добавить явное ожидание (тема урока 07)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "dynamic-content"))
)

Ошибка 2: By.CLASS_NAME с составным классом

Симптом: InvalidSelectorException: Compound class names not permitted.

Причина: By.CLASS_NAME принимает только одно имя класса без пробелов. Если у элемента несколько классов, разделённых пробелом — это разные классы.

# HTML
<button class="btn btn-primary">Войти</button>

# НЕВЕРНО — InvalidSelectorException
element = driver.find_element(By.CLASS_NAME, "btn btn-primary")

# ВЕРНО — CSS_SELECTOR с точками
element = driver.find_element(By.CSS_SELECTOR, ".btn.btn-primary")

# Также верно — по одному классу
element = driver.find_element(By.CLASS_NAME, "btn-primary")

Ошибка 3: Хрупкий XPath из DevTools «Copy XPath»

Симптом: локатор работает сегодня, но ломается после любого изменения разметки или стиля страницы.

Причина: DevTools генерирует абсолютный XPath по позиции в DOM.

# Автоматически скопированный XPath из DevTools — хрупкий
/html/body/div[2]/main/div/form/div[1]/input  # сломается при любом изменении DOM

# ЛУЧШЕ: XPath на основе стабильного атрибута
//*[@id="username"]                # по ID
//input[@name="username"]          # по name
//button[text()="Войти"]           # по тексту
Правило: никогда не используйте в продакшн-тестах XPath вида /html/body/div[N]/.... Такой локатор сломается при любом добавлении/удалении элемента на странице.

Ошибка 4: Путаница find_element и find_elements

Симптом 1: AttributeError: 'list' object has no attribute 'text' — попытка вызвать .text у списка.

Симптом 2: тест не падает, хотя элемент отсутствует — find_elements вернул [].

# ПРОБЛЕМА: find_elements возвращает список, не WebElement
cards = driver.find_elements(By.CSS_SELECTOR, ".col-sm-4")
print(cards.text)  # AttributeError! У списка нет .text

# РЕШЕНИЕ: обращаться к элементу по индексу
print(cards[0].text)  # текст первой карточки

# ПРОБЛЕМА: пропущенная проверка на пустой список
cards = driver.find_elements(By.CSS_SELECTOR, ".non-existent")
print(cards[0].text)  # IndexError: list index out of range

# РЕШЕНИЕ: проверить длину
if cards:
    print(cards[0].text)
else:
    print("Элементов не найдено")

Ошибка 5: is_displayed() на несуществующем элементе

Симптом: думаете проверить "есть ли элемент", но используете is_displayed() после find_element() — который уже выбросил исключение.

# НЕВЕРНО: если элемент не найден, find_element выбросит
# NoSuchElementException ещё до вызова is_displayed()
element = driver.find_element(By.ID, "maybe-exists")
element.is_displayed()  # до этой строки не дойдёт!

# ВЕРНО: используйте find_elements для безопасной проверки
elements = driver.find_elements(By.ID, "maybe-exists")
if elements and elements[0].is_displayed():
    print("Элемент найден и виден")
elif elements:
    print("Элемент есть в DOM, но скрыт")
else:
    print("Элемента нет в DOM")

Ошибка 6: XPath с @class — неполное совпадение

Симптом: //*[@class="btn"] не находит <button class="btn btn-primary">.

Причина: @class="btn" ищет точное совпадение всего значения атрибута class.

# HTML
<button class="btn btn-primary">Click</button>

# НЕ НАЙДЁТ — ищет точное "btn"
//*[@class="btn"]

# НАЙДЁТ — contains проверяет подстроку
//*[contains(@class, "btn")]

# Или CSS (рекомендуется):
.btn.btn-primary

Ошибка 7: Использование старого API (Selenium 3)

Подробнее — в разделе ⚖️ Старый vs Новый.

# ПАДАЕТ в Selenium 4
driver.find_element_by_id("x")  # AttributeError

# ВЕРНО
driver.find_element(By.ID, "x")