Page Object Model

Когда тестов становится много, локаторы и действия выносят в отдельные классы — это паттерн Page Object.

Page Object Model (POM) — паттерн, при котором каждая страница приложения описывается классом: внутри — её локаторы и действия, а тесты вызывают понятные методы.

Проблема, которую решает POM

Допустим, локатор кнопки входа повторяется в двадцати тестах. Дизайнер переименовал кнопку — придётся править двадцать мест. А если каждый тест ещё и сам прописывает шаги входа, дублирование разрастается. POM собирает всё, что связано со страницей, в одном месте, и тесты перестают зависеть от деталей вёрстки.

Как выглядит Page Object

Класс страницы хранит ссылку на page, объявляет локаторы и предоставляет методы-действия с понятными именами.

// pages/LoginPage.ts
import { Page, Locator, expect } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Пароль');
    this.submitButton = page.getByRole('button', { name: 'Войти' });
    this.errorMessage = page.getByRole('alert');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectError(text: string) {
    await expect(this.errorMessage).toHaveText(text);
  }
}

Тест становится читаемым

Теперь тест говорит о поведении, а не о кнопках и полях. Технические детали спрятаны в Page Object.

import { test } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';

test('ошибка при неверном пароле', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('[email protected]', 'wrong-password');
  await loginPage.expectError('Неверный email или пароль');
});

Чем это хорошо

СвойствоПольза
Локаторы в одном местесмена вёрстки — правка одного файла
Понятные методытест читается как сценарий
Переиспользованиеlogin() вызывается из многих тестов

Когда POM нужен, а когда нет

POM окупается, когда тестов много и они работают с одними и теми же страницами. Для пары простых тестов он избыточен — добавит слоёв без пользы. Не превращайте Page Object в «свалку»: метод вроде fillEmailThenClickSubmitThenWait() — признак того, что вы складываете в класс саму логику теста. Page Object описывает страницу (какие на ней элементы и какие базовые действия), а сценарий — что и в каком порядке проверять — остаётся в тесте.

POM и фикстуры вместе

На практике Page Object часто отдают через фикстуру из прошлого урока — тогда тест получает готовый объект страницы и не создаёт его вручную. Это сочетание двух приёмов: фикстура управляет жизненным циклом, Page Object — структурой страницы.

Итог

  • POM выносит локаторы и действия страницы в отдельный класс.
  • Тесты вызывают понятные методы (login, expectError) вместо повторения локаторов.
  • Смена вёрстки правится в одном месте — в классе страницы.
  • Применяйте POM при большом числе тестов; не складывайте в него логику сценариев.
Проверьте себя
1. Что описывает класс в паттерне Page Object Model?
AКонфигурацию всего проекта
BЛокаторы и действия одной страницы приложения
CСписок всех тестов
DНастройки браузера
2. В чём главное преимущество POM при смене вёрстки?
AТесты начинают работать быстрее
BЛокатор правится в одном месте — в классе страницы
CБраузер перестаёт требовать ожиданий
DТесты можно не запускать
3. Что считается неправильным использованием Page Object?
AХранить в нём локаторы страницы
BСкладывать в него логику конкретного сценария теста
CДавать методам понятные имена
DИспользовать его в нескольких тестах
Поддержать проект