Best practices для селекторов

Селекторы являются основой автоматизированного тестирования в Cypress, так как именно через них происходит взаимодействие с элементами страницы. Правильный выбор и использование селекторов напрямую влияет на стабильность, читаемость и поддержку тестов.


Использование уникальных идентификаторов

Наиболее надёжный способ обращения к элементу — использование уникальных атрибутов, таких как id или специально созданные для тестов data-* атрибуты:

<button id="submit-login">Войти</button>
<button data-cy="submit-login">Войти</button>

В Cypress:

cy.get('#submit-login').click();
cy.get('[data-cy=submit-login]').click();

Ключевые моменты:

  • id уникален на странице, что делает селектор точным.
  • data-* атрибуты создаются специально для тестов и не зависят от визуального представления или структуры DOM.
  • Использование таких атрибутов снижает риск поломки тестов при изменении классов или структуры HTML.

Избегать зависимостей от классов и вложенности

Селекторы по классам и сложной структуре DOM выглядят удобно, но часто оказываются хрупкими:

cy.get('.form > .input-group > input').type('test'); // ненадежно

Проблемы:

  • Любое изменение верстки ломает тест.
  • Многоуровневая вложенность затрудняет чтение и поддержку.

Рекомендация: использовать классы только как fallback, если нет уникальных идентификаторов или data-* атрибутов.


Явные и читаемые селекторы

Селектор должен ясно отражать назначение элемента. Например, вместо:

cy.get('[data-cy=btn-1]').click();

лучше использовать:

cy.get('[data-cy=submit-login]').click();

Это облегчает понимание теста без необходимости открывать HTML.


Использование cy.contains для текстовых элементов

Для кнопок и ссылок, текст которых уникален и не меняется, удобно использовать cy.contains:

cy.contains('Войти').click();
cy.contains('Забыли пароль?').should('be.visible');

Особенности:

  • Позволяет тесту быть менее чувствительным к структуре DOM.
  • Не рекомендуется использовать, если текст часто меняется или переводится.

Комбинированные селекторы

В случаях, когда уникальных атрибутов недостаточно, можно комбинировать селекторы:

cy.get('[data-cy=form-login] input[name=email]').type('test@example.com');

Плюсы:

  • Явно указывает контекст.
  • Снижает вероятность попадания на другой элемент с таким же именем или классом.

Минусы:

  • Сложные цепочки могут быть хрупкими при изменении структуры.
  • Следует использовать умеренно и только при необходимости уточнения контекста.

Стратегия «тестируем через API атрибутов, а не через визуальные классы»

Cypress позволяет тестировать элементы независимо от стилей. Это означает, что:

  • Не следует использовать селекторы по цвету, шрифту или положению.
  • Классы, отвечающие только за оформление (btn-primary, text-center), лучше не применять как основной селектор.

Пример плохой практики:

cy.get('.btn-primary').click(); // сломается при смене темы

Пример правильной практики:

cy.get('[data-cy=submit-login]').click();

Использование алиасов для повторного доступа

Если элемент используется несколько раз в тесте, можно назначить алиас:

cy.get('[data-cy=submit-login]').as('submitButton');
cy.get('@submitButton').click();
cy.get('@submitButton').should('be.disabled');

Преимущества:

  • Улучшает читаемость.
  • Снижает дублирование кода.
  • Облегчает поддержку при изменении селектора.

Проверка существования и видимости

Перед взаимодействием с элементом рекомендуется проверять его состояние:

cy.get('[data-cy=submit-login]').should('exist').and('be.visible').click();

Это гарантирует, что тест не упадет из-за отсутствия элемента на странице в момент выполнения действия.


Поддержка селекторов при изменениях

Чтобы тесты были устойчивыми к изменениям интерфейса:

  1. Использовать data-* атрибуты для критичных элементов.
  2. Обновлять селекторы централизованно через константы или Page Object.
  3. Минимизировать вложенные и сложные цепочки селекторов.
  4. Избегать тестирования через визуальные характеристики.

Пример использования Page Object для селекторов:

// loginPage.js
export const loginPage = {
  emailInput: '[data-cy=email]',
  passwordInput: '[data-cy=password]',
  submitButton: '[data-cy=submit-login]',
};

// тест
import { loginPage } from './loginPage';
cy.get(loginPage.emailInput).type('user@example.com');
cy.get(loginPage.passwordInput).type('password');
cy.get(loginPage.submitButton).click();

Это делает тесты более структурированными, упрощает обновление селекторов и улучшает читаемость.


Эти практики обеспечивают стабильность тестов, их поддержку и понимание при командной разработке. Селекторы должны быть предсказуемыми, устойчивыми к изменениям интерфейса и удобными для чтения.