Стратегии локаторов
Updated Jul 2026
Выбор правильной стратегии локаторов — одно из наиболее влиятельных решений в автоматизации тестирования. Хрупкие локаторы — главная причина бремени поддержки тестов. Playwright предоставляет пользователе-ориентированные локаторы, которые отражают то, как реальные пользователи находят элементы — и они значительно стабильнее CSS/XPath-селекторов, используемых в Selenium.
Иерархия локаторов
Playwright рекомендует локаторы в следующем порядке приоритета:
| Приоритет | Локатор | Пример | Почему |
|---|---|---|---|
| 1 | Роль | getByRole('button', { name: 'Отправить' }) |
Отражает дерево доступности; устойчив к изменениям разметки |
| 2 | Текст | getByText('Войти') |
Видимый пользователю; ломается только при изменении текста |
| 3 | Test ID | getByTestId('login-form') |
Явный контракт между разработчиком и тестировщиком |
| 4 | Метка | getByLabel('Электронная почта') |
Поля форм через связанный label |
| 5 | Placeholder | getByPlaceholder('Введите email') |
Поля ввода без видимых меток |
| 6 | CSS/XPath | page.locator('.btn-primary') |
Последний вариант для сложного обхода DOM |
Ролевые локаторы (предпочтительные)
Ролевые локаторы используют дерево доступности, а не структуру DOM. Они находят элементы так, как это делает программа чтения с экрана — что означает устойчивость к рефакторингу HTML.
// Находит <button>, <input type="submit"> или любой элемент с role="button"
await page.getByRole('button', { name: 'Отправить' }).click();
// Находит элементы <a> или элементы с role="link"
await page.getByRole('link', { name: 'Регистрация' }).click();
// Находит <h1>–<h6> или элементы с role="heading"
await expect(page.getByRole('heading', { name: 'Панель управления' })).toBeVisible();
// Чекбоксы и радиокнопки
await page.getByRole('checkbox', { name: 'Запомнить меня' }).check();
Текстовые локаторы
// Точный текст
await page.getByText('С возвращением', { exact: true }).click();
// Частичное совпадение (по умолчанию)
await page.getByText('С возвращением').click();
// Регулярное выражение
await page.getByText(/добро пожаловать/i).click();
Локаторы Test ID
Когда ролевые и текстовые локаторы неприменимы (например, элементы без значимого текста или несколько идентичных элементов), test ID обеспечивают стабильный контракт.
// В приложении: <div data-testid="user-profile">...</div>
await page.getByTestId('user-profile').click();
// Настройка имени атрибута в playwright.config.ts:
// use: { testIdAttribute: 'data-qa' }
Локаторы по метке и placeholder
// Поиск input по связанному <label>
await page.getByLabel('Электронная почта').fill('user@example.com');
// Поиск input по тексту placeholder
await page.getByPlaceholder('Поиск товаров').fill('ноутбук');
CSS и XPath: когда они нужны
Playwright поддерживает CSS и XPath для случаев, когда семантические локаторы недостаточны:
// CSS-селектор
await page.locator('.product-card >> .price').textContent();
// XPath (с префиксом //)
await page.locator('//div[@class="sidebar"]//a').click();
Когда CSS/XPath допустимы
- Сторонние виджеты без доступных ролей или test ID
- Сложный обход DOM (родитель → потомок → сосед)
- Унаследованные приложения, где добавление test ID невозможно
Когда избегать CSS/XPath
- Позиционные селекторы вроде
div:nth-child(3)— ломаются при изменении порядка разметки - Глубокая вложенность вроде
#app > div > main > section > form > button— ломается при любом рефакторинге - Селекторы по классам, нацеленные на стилизацию (
.btn-primary), а не на назначение
Сравнение стратегий локаторов
| Стратегия | Стабильность | Читаемость | Поддержка | Лучше всего для |
|---|---|---|---|---|
getByRole() |
Высокая | Высокая | Низкая | Кнопки, ссылки, заголовки, элементы форм |
getByText() |
Средняя | Высокая | Низкая | Статический контент, метки |
getByTestId() |
Высокая | Средняя | Низкая | Динамический контент, сложные компоненты |
getByLabel() |
Высокая | Высокая | Низкая | Поля ввода форм |
| CSS-селектор | Низкая–Средняя | Низкая | Высокая | Сторонние виджеты, устаревшие приложения |
| XPath | Низкая | Низкая | Высокая | Сложный обход, поиск по тексту |
Практический пример: форма входа
// ЛУЧШЕ ВСЕГО: ролевые локаторы
await page.getByLabel('Email').fill('user@example.com');
await page.getByLabel('Password').fill('secret');
await page.getByRole('button', { name: 'Sign in' }).click();
// ХОРОШО: test ID локаторы
await page.getByTestId('email-input').fill('user@example.com');
await page.getByTestId('password-input').fill('secret');
await page.getByTestId('login-button').click();
// ИЗБЕГАЙТЕ: CSS-селекторы, привязанные к реализации
await page.locator('input[type="email"]').fill('user@example.com');
await page.locator('input[type="password"]').fill('secret');
await page.locator('form.login-form button.btn-primary').click();
Ключевые выводы
- Предпочитайте ролевые локаторы (
getByRole) — они отражают поведение пользователя и выживают при рефакторинге - Используйте
getByTestId, когда семантические локаторы недоступны — это создаёт стабильный тестовый контракт - Оставляйте CSS/XPath для сторонних виджетов и устаревших приложений
- Избегайте позиционных селекторов, глубокой вложенности и селекторов по классам стилизации
- Хорошая стратегия локаторов сокращает поддержку тестов на 60–80% по сравнению с тестами, насыщенными CSS/XPath