Локатор говорит фреймворку, какой элемент найти. Большинство «сломанных» 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?