Page Object Model — способ отделить работу с интерфейсом от смысла тестового сценария. Page Object знает, как найти поле, заполнить форму и дождаться результата. Сам тест объясняет, какое поведение проверяется и почему результат считается правильным.
Паттерн полезен, когда несколько сценариев используют одинаковые элементы или действия. После изменения разметки команда обновляет локатор в одном месте. Но если объект страницы скрывает всю бизнес-логику и сотни проверок, поддержка становится сложнее: тест выглядит коротким, а причина отказа теряется внутри общего кода.
Граница ответственности объекта
Хороший объект предоставляет действия и наблюдения: ввести адрес, отправить форму, получить сообщение об ошибке. Ему не обязательно соответствовать целой странице. Шапку, корзину и диалог подтверждения часто удобнее выразить отдельными компонентами с собственными локаторами.
У объекта должна быть понятная область поиска. Две кнопки «Сохранить» в разных диалогах нельзя различать случайным первым совпадением. Сначала выбирают нужный контейнер, затем элемент внутри него. Такое решение отражает структуру пользовательского взаимодействия и уменьшает хрупкость.
Где размещать проверки
Сценарий должен явно показывать свой главный ожидаемый результат. Если тест называется «показывает ошибку пустого имени», рядом с действием должна быть проверка конкретного сообщения. Объект может предоставлять locator сообщения, но не обязан всегда проверять успешный исход после любого submit.
Внутри объекта допустима проверка готовности, например наличие основного элемента экрана. Это техническое предусловие. Его важно не путать с бизнес-ожиданием: один и тот же метод отправки формы может использоваться и в положительном, и в отрицательном сценарии.
В отрицательном сценарии метод save может вернуть состояние формы, не выбрасывая исключение из-за отсутствия успешного сообщения. При этом техническая ошибка, например исчезнувшая кнопка, должна оставаться видимой. Не заменяйте её универсальным возвратом false: такой ответ стирает различие между ожидаемым отказом продукта и поломкой самого взаимодействия. Объект предоставляет факты, а сценарий решает, соответствуют ли они конкретному ожиданию. Это позволяет использовать одну форму для проверки ошибок и успешного сохранения, не добавляя флагов skipAssertion или expectFailure.
Минимальный самостоятельный пример
Пример использует Playwright Test и локальную HTML-страницу в памяти браузера. После установки @playwright/test и подходящего браузера сохраните файл как profile.spec.js и запустите npx playwright test profile.spec.js. Внешний сайт, аккаунт и сервер не нужны; учебная форма намеренно проста.
const { test, expect } = require('@playwright/test');
class ProfileForm {
constructor(page) {
this.name = page.getByLabel('Имя');
this.save = page.getByRole('button', { name: 'Сохранить' });
this.message = page.getByRole('status');
}
async submit(name) {
await this.name.fill(name);
await this.save.click();
}
}
test('не сохраняет пустое имя', async ({ page }) => {
await page.setContent(`
<label>Имя <input id="name"></label>
<button id="save">Сохранить</button><p role="status"></p>
<script>
document.querySelector('#save').onclick = () => {
const name = document.querySelector('#name').value.trim();
document.querySelector('[role=status]').textContent =
name ? 'Сохранено' : 'Введите имя';
};
</script>`);
const form = new ProfileForm(page);
await form.submit('');
await expect(form.message).toHaveText('Введите имя');
});Код демонстрирует разделение обязанностей: объект знает взаимодействие, тест задаёт ожидаемую ошибку. Он не доказывает сохранение профиля в реальном продукте. Для этого нужен другой сценарий с настоящим API и отдельными данными, а не расширение учебной проверки скрытыми предположениями.
Практический сценарий реального проекта
В тестовом личном кабинете создайте отдельного пользователя с именем Анна. Откройте редактирование, замените имя пробелами и отправьте форму. Ожидайте понятную ошибку и отсутствие успешного сохранения. Затем введите Мария и проверьте подтверждение, повторное открытие и фактическое сохранённое значение.
Первый и второй исход могут использовать один объект формы. При этом API или повторное чтение дают отдельное свидетельство сохранения. Проверка только текста «Сохранено» пропустит ситуацию, когда frontend показывает успех, а backend не изменил запись.
Локаторы и ожидания
Предпочитайте устойчивые признаки: роль и доступное имя, label, согласованный test id. Длинный CSS-путь через несколько вложенных div привязывает тест к вёрстке. Текст тоже требует осознанности: если продукт локализован, локаль окружения должна быть задана явно.
Ожидание должно соответствовать состоянию. После сохранения ждут подтверждение или изменение данных, а не фиксированную паузу. Page Object не исправляет синхронизацию автоматически: спрятанный sleep остаётся таким же источником нестабильности, как sleep непосредственно в тесте.
Почему опасны большие базовые классы
Общий BasePage с десятками универсальных методов быстро превращается в дополнительный фреймворк. Метод clickElementByAnyLocator скрывает полезные возможности инструмента и усложняет ошибку. Наследование каждой страницы от общего гиганта создаёт связи между несвязанными сценариями.
Выделяйте повторяемость после появления реальных примеров. Небольшой компонент адресной формы полезнее универсального механизма «все формы продукта». Композиция позволяет подключать один компонент в разных экранах без иерархии, отражающей случайные технические совпадения.
Данные и состояние
Не храните общий browser или пользователя в статической переменной объекта страницы. Новый сценарий должен получить свой контекст и контролируемые данные. Иначе параллельные тесты начинают менять одни и те же поля и объяснить падение становится трудно.
Конструктор желательно оставлять предсказуемым: объявление локаторов не должно незаметно логиниться, удалять данные или открывать несколько страниц. Подготовка, переход и проверка должны быть видны на подходящем уровне сценария или fixture.
Как проверить качество архитектуры
Возьмите небольшое изменение интерфейса и посмотрите, сколько файлов потребуется изменить. Затем прочитайте упавший отчёт без открытия реализации объекта. Должно быть понятно, какое действие выполнялось, какой результат ожидали и какой элемент не найден.
Полезный критерий — тест читается как последовательность целей, а объект остаётся небольшим и переиспользуемым. Если для понимания одной строки нужно пройти пять уровней наследования, абстракция перестала помогать.
Типичные ошибки и выводы
Частые ошибки: создавать класс для каждой HTML-страницы автоматически, прятать assertions в действия, возвращать булево false вместо полезного исключения и строить объект вокруг CSS, а не пользовательских возможностей. Паттерн также не решает проблемы общего стенда и плохих тестовых данных.
Page Object Model полезен как дисциплина границ. Он сокращает повторение взаимодействий, оставляет ожидания понятными и помогает изолировать изменения интерфейса. Начинайте с простых объектов и развивайте их по мере реальных потребностей набора.