Урок по треку Автоматизация тестирования 7 мин

Локаторы без боли за 7 минут

CSS, XPath, роли и data-testid: как выбрать устойчивый локатор.

Локатор говорит фреймворку, какой элемент найти. Большинство «сломанных» UI-тестов ломаются не из-за багов, а из-за локаторов, которые зависели от вёрстки.

Виды локаторов

  • id: #login. Быстро и коротко, если id стабильный. Плохо, если он сгенерирован (:r3:, input-1734).
  • CSS: form.login button[type="submit"]. Читается легко, работает быстро. Не умеет искать по тексту.
  • XPath: //button[normalize-space()="Войти"]. Умеет искать по тексту, ходить к предкам (ancestor::) и соседям (following-sibling::). Многословнее.
  • Атрибуты для тестов: [data-testid="login-submit"]. Не зависят от стилей и текста, но их надо договориться проставлять.
  • По роли и доступному имени (Playwright, Testing Library): getByRole('button', { name: 'Войти' }). Ищут элемент так, как его видит пользователь и скринридер.

Что ломается

/html/body/div[2]/div/div[3]/form/button      абсолютный XPath, любая обёртка его ломает
.css-1x2y3z                                    хеш CSS-in-JS, меняется при сборке
div > div > div > span:nth-child(4)            завязка на структуру и порядок
//button[text()="Войти "]                      лишний пробел, перевод строки или вложенный span

Устойчивый локатор опирается на смысл: роль, подпись, специальный атрибут. Хрупкий опирается на структуру и оформление.

Приоритет на практике

В Playwright документация советует начинать с пользовательских локаторов: getByRole, getByLabel, getByPlaceholder, getByText, и брать getByTestId там, где смысловых зацепок нет. Атрибут по умолчанию data-testid, его можно поменять настройкой testIdAttribute.

В Selenium нет поиска по роли из коробки, поэтому команды договариваются о data-testid или data-qa и пишут CSS по ним.

Тонкости XPath

  • text() берёт только прямой текстовый узел. Для <button><span>Войти</span></button> нужен . или normalize-space(): //button[normalize-space()="Войти"].
  • //li[2] это «каждый li, который второй у своего родителя». (//li)[2] это «второй li во всём документе». Разные вещи.
  • contains(@class, "btn") совпадёт и с btn-primary. Для точного класса: contains(concat(" ", normalize-space(@class), " "), " btn ").
  • Строку с кавычкой нельзя просто подставить в '...': нужен concat().

Строгость в Playwright

Локатор Playwright ленивый: элемент ищется в момент действия. Если под локатор попало несколько элементов, действие (click, fill) упадёт с ошибкой strict mode violation. Это защита: тест не кликнет «не туда». Уточняйте локатор (filter({ hasText }), цепочка от контейнера) вместо .first() по привычке.

Selenium 4: относительные локаторы

RelativeLocator.with(By.tagName("input")).below(By.id("email")) ищет элемент по положению относительно другого. Удобно для форм без хороших атрибутов, но зависит от раскладки.

Частые ошибки

  • Копировать XPath из DevTools («Copy full XPath») прямо в тест.
  • Искать по классам оформления и сгенерированным id.
  • Брать .first() или индекс, чтобы обойти неоднозначность.
  • Искать по тексту, который меняется (локализация, A/B-тест).

Что спросят на собесе

  • Какие локаторы вы предпочитаете и почему?
  • Чем CSS отличается от XPath, что умеет только XPath?
  • В чём разница //li[2] и (//li)[2]?
  • Что делать, если у элемента нет ни id, ни testid?
  • Что такое strict mode в Playwright?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.