Локаторы определяют способ поиска веб-элементов в DOM и образуют фундамент надёжных UI-тестов. Ошибки в выборе локаторов приводят к нестабильности тестов, ложным падениям и долгому сопровождению. Поэтому подход к локаторам должен быть системным и строгим.
DOM часто изменяется из-за рефакторинга, обновления фреймворков,
правок вёрстки или смены UI-библиотек. Локаторы, завязанные на структуру
или стили, разрушаются быстрее всего. Например, выборка по
div:nth-child(3) или span[class*="blue"]
ломается после минимальной корректировки макета. Такие селекторы
увеличивают плотность ложных ошибок и снижают ценность
автотестирования.
Независимость от внешних изменений. Локатор должен устойчиво переживать косметические и структурные правки.
Чёткая семантика. Локатор должен отражать смысл элемента, а не способ его отображения.
Предсказуемость и однозначность. Локатор обязан находить ровно один элемент, если тест подразумевает уникальность.
Наиболее надёжным источником локаторов считаются специально введённые
тестовые атрибуты. В SPA-приложениях на Angular традиционно используют
data-test-id, data-testid,
data-e2e, data-qa и аналогичные.
Пример устойчивого селектора в Protractor:
const loginButton = element(by.css('[data-test-id="login-button"]'));
Подобный подход даёт разработчикам и тестировщикам общий контракт: значение атрибута фиксируется и меняется осознанно. При этом тестовые атрибуты не влияют на визуальную часть и не зависят от CSS.
Protractor поддерживает оба подхода, однако CSS-селекторы на практике предпочтительнее из-за лаконичности, скорости и лучшей поддержки инструментов разработки. XPath применяют только при отсутствии семантической опоры в DOM.
CSS: • проще читается; • быстрее интерпретируется; • естественнее описывает структуру и атрибуты.
XPath: • тяжёл в сопровождении; • нередко завязан на структуру; • полезен при сложных вложенных отношениях или работе с текстом.
by.model, by.binding и специализированных
локаторов AngularИсторически Protractor предлагал специальные локаторы для Angular:
by.model, by.binding, by.repeat и
т.д. Они облегчали обращение к Angular-концептам, но привязывали тесты к
конкретному фреймворку. В современных проектах всё чаще предпочитают
CSS-селекторы с тест-атрибутами, что обеспечивает переносимость и более
чистую изоляцию слоёв.
Локаторы по видимому тексту используют редко и осторожно. Текст чувствителен к локализации, правкам UX-копирайтинга и контекстным изменениям. Мягкие сигналы интерфейса, такие как placeholder или tooltip, ещё менее надёжны. Если текст — единственный доступный идентификатор, предпочтительно задавать его в тестовом атрибуте, а не считывать напрямую из DOM.
Для компонентов, состоящих из множества подэлементов (например, комбобоксы, таблицы, datepicker), важно задавать локаторы для каждого уровня:
Таким образом разрушается зависимость от временных структур. Это решает типичную проблему тестов, которые ломаются вслед за изменением вёрстки даже при одинаковом поведении интерфейса.
Обращение к элементам по индексу
(element.all(...).get(2)) допускается только там, где
порядок гарантирован бизнес-логикой, а не HTML-структурой. В остальных
случаях требуется уникальный идентификатор или дополнительный атрибут.
Индексы полезны лишь как средство навигации в коллекциях для проверки
сортировки, пагинации или фильтрации, а не как постоянная опора
локаторов.
Композиция позволяет локализовать элемент в рамках узкого контекста, уменьшая риск пересечения похожих элементов. Например:
const table = element(by.css('[data-test-id="users-table"]'));
const firstRow = table.element(by.css('[data-test-id="row"]'));
const deleteButton = firstRow.element(by.css('[data-test-id="delete"]'));
Контекстная композиция повышает стабильность тестов на страницах с повторяющимися компонентами и уменьшает вероятность случайного совпадения локаторов.
CSS-классы остаются популярным способом идентификации, но в реальных проектах классы часто используются для стилизации и меняются при редизайне. Исключение — специально зарезервированные QA-классы с фиксированным контрактом, применяемые аналогично тест-атрибутам. Такие классы должны быть явно определены и документированы.
При эволюции интерфейса тестовые атрибуты переходят в состояние устаревших. Чтобы избежать хаотичных правок, в командах практикуют версионирование атрибутов, временное поддержание старых значений и уведомление тестовой части при изменениях.
Жизненный цикл локаторов включает добавление, поддержку, рефакторинг и удаление. Надёжность автотестов существенно возрастает, когда локаторы проектируются как часть API компонентов. Такое отношение делает UI предсказуемым для автоматизации, аналогично тому, как REST-контракт делает предсказуемым веб-сервис.
Page Object изолирует локаторы от логики тестов. При грамотном проектировании изменения в DOM затрагивают только слой локаторов, не затрагивая тестовые сценарии. Надёжность локаторов усиливается, если Page Object не экспортирует их наружу напрямую, а предоставляет методы, скрывающие детали получения элементов.
• Устойчивость выше краткости. Короткий селектор, который легко сломать, хуже длинного, но стабильного.
• Текст и стиль — слабая основа. Надёжность локаторов не должна зависеть от UI-косметики.
• Контракт важнее структуры. Фиксированный атрибут сильнее, чем путь в DOM.
• Локаторы — часть архитектуры. Документирование и договорённости между разработчиками и тестировщиками увеличивают срок службы тестов.
При соблюдении описанных принципов автотесты становятся менее хрупкими, быстрее сопровождаются и реже падают по несущественным причинам. Надёжные локаторы формируют основу предсказуемых и долговечных тестовых пакетов, что особенно критично при масштабировании регрессии и переходе к непрерывной интеграции.