Page Object Pattern в 2026: классика, эволюция и когда не нужен
Page Object Pattern (POP) — самый известный паттерн в автоматизации UI-тестов. Идея 2005 года: вместо того чтобы тесты лезли в DOM напрямую, заворачиваем UI в классы, и тесты работают с методами, а не с CSS-селекторами. За 20 лет паттерн прошёл через эволюцию — что использовать сегодня.
Базовый Page Object
class LoginPage {
constructor(private page: Page) {}
async login(email: string, password: string) {
await this.page.getByLabel('Email').fill(email);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: 'Sign In' }).click();
}
}
Тест:
test('login works', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login('user@example.com', 'pass123');
await expect(page).toHaveURL('/dashboard');
});
Плюсы — изменился UI? Меняешь только Page Object, тесты остаются.
Антипаттерны Page Object
❌ Возврат raw locator’ов: loginPage.emailField → тест дёргает loginPage.emailField.fill(...). Это инкапсуляция нарушена, тест опять работает с DOM-уровнем.
❌ Слишком жирные методы: loginAndCreateUserAndVerifyDashboard — тест с одной строкой кода. Нечитаемо, нельзя переиспользовать части.
❌ Asserts внутри Page Object: loginPage.expectErrorMessage('Invalid') — тогда Page Object знает что тестируешь. Asserts — в тесте.
❌ Иерархия наследования: class CheckoutPage extends BasePage extends Component. Глубоко — путаница. Composition over inheritance.
Эволюция: Screenplay Pattern
Альтернатива POP — Screenplay Pattern (Antony Marcano, 2014). Вместо «страница имеет действия» — «актёр выполняет задачи, отвечая на вопросы». Используется в Serenity BDD.
actor.attemptsTo(
Navigate.toLoginPage(),
Enter.value('user@example.com').into(EmailField),
Enter.value('pass123').into(PasswordField),
Click.on(SignInButton),
);
actor.shouldSee(
CurrentUrl.equals('/dashboard'),
);
Плюс — лучше масштабируется на много страниц/действий. Минус — больше overhead на старте.
В моб. геймдеве POP всё ещё доминирует — Screenplay скорее для крупных enterprise-проектов.
Современный POP с TypeScript + Playwright
Лучшие практики 2026:
— Fixtures вместо new Page(): test.extend({ loginPage: ... }) → тест получает готовый Page Object автоматически.
— Без иерархии: каждый Page Object — самостоятельный класс. Общие хелперы — utility-функции, не базовый класс.
— Locators в getter’ах, не в constructor: позволяет lazy-resolve.
— Composable: CheckoutPage имеет paymentSection: PaymentSection — нестинг компонентов, не наследование.
Когда POP НЕ нужен
— Один-два теста на компонент: создавать класс ради двух вызовов — overengineering.
— API-тесты: для API лучше Service Object Pattern (один класс на endpoint group).
— Visual regression: там не важна логика взаимодействия, важны скриншоты.
— Smoke-тесты на Maestro: YAML-флоу проще читать без абстракций.
Действия для команды
✅ Audit’ни существующие Page Objects → найди те с >10 методов или >300 строк. Это кандидаты на разбиение по компонентам.
✅ Проверь — нет ли asserts внутри Page Objects. Если есть — вынеси в тесты.
✅ Введи правило «один класс = одна страница / один компонент». Не «один класс на весь app».
Подробнее: Martin Fowler — Page Object, Playwright POM guide.