automationplaywright

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.