Page Object Pattern в тестах

Page Object Pattern (POP) — это архитектурный подход к организации тестов пользовательского интерфейса, который позволяет повысить читаемость, поддерживаемость и повторное использование кода. Идея заключается в том, чтобы представлять страницы или компоненты приложения как объекты с методами и свойствами, отражающими действия и элементы на странице. В контексте тестирования с использованием Enzyme этот паттерн помогает изолировать логику взаимодействия с компонентами React от самих тестов.


Основные принципы Page Object Pattern

1. Абстракция взаимодействия с компонентом Каждый Page Object инкапсулирует детали работы с элементами DOM. Тесты не должны напрямую обращаться к селекторам, а использовать методы Page Object.

Пример:

class LoginPage {
  constructor(wrapper) {
    this.wrapper = wrapper;
  }

  setUsername(username) {
    this.wrapper.find('input[name="username"]').simulate('change', { target: { value: username } });
  }

  setPassword(password) {
    this.wrapper.find('input[name="password"]').simulate('change', { target: { value: password } });
  }

  submit() {
    this.wrapper.find('form').simulate('submit');
  }

  getErrorMessage() {
    return this.wrapper.find('.error-message').text();
  }
}

В этом примере тестовый код не зависит от конкретной реализации формы: любые изменения селекторов или DOM можно исправить только внутри Page Object.

2. Методы должны отражать действия пользователя Методы Page Object описывают именно действия или запросы информации, а не внутренние детали DOM. Это делает тесты понятными и декларативными:

loginPage.setUsername('user1');
loginPage.setPassword('password123');
loginPage.submit();
expect(loginPage.getErrorMessage()).toBe('Invalid credentials');

Организация Page Objects для Enzyme

1. Разделение по страницам и компонентам Каждая страница или крупный компонент имеет отдельный файл Page Object. Например:

/tests/pageObjects/LoginPage.js
/tests/pageObjects/DashboardPage.js

Если компонент содержит вложенные подкомпоненты, можно создавать отдельные объекты для них и использовать композицию:

class DashboardPage {
  constructor(wrapper) {
    this.wrapper = wrapper;
    this.header = new HeaderComponent(wrapper.find('Header'));
    this.sidebar = new SidebarComponent(wrapper.find('Sidebar'));
  }

  getUserGreeting() {
    return this.header.getGreeting();
  }

  navigateTo(section) {
    this.sidebar.clickSection(section);
  }
}

2. Инициализация Page Object Page Object получает на вход обертку Enzyme (mount или shallow). Использование mount предпочтительнее для тестирования полной интеграции с жизненным циклом компонентов.

import { mount } from 'enzyme';
import LoginPage from './pageObjects/LoginPage';
import App from '../App';

const wrapper = mount(<App />);
const loginPage = new LoginPage(wrapper.find('LoginForm'));

Взаимодействие с элементами

1. Симуляция событий Enzyme предоставляет методы .simulate() для имитации событий, таких как click, change, submit. Page Object инкапсулирует эти вызовы:

submitForm() {
  this.wrapper.find('form').simulate('submit');
}

2. Получение данных из компонента Методы для чтения состояния страницы или сообщений об ошибках используют .text(), .prop(), .state():

getUsernameFieldValue() {
  return this.wrapper.find('input[name="username"]').prop('value');
}

3. Работа с вложенными компонентами Если компонент состоит из нескольких вложенных компонентов, Page Object может создавать объекты для них и вызывать их методы:

this.wrapper.find('Header').simulate('click');

или

this.header.clickLogoutButton();

Преимущества использования Page Object Pattern

  1. Повышенная поддерживаемость — изменения в DOM отражаются только в Page Object, тесты остаются неизменными.
  2. Читаемость тестов — тесты становятся декларативными, легче понять сценарий.
  3. Повторное использование — одни и те же методы Page Object могут использоваться в разных тестах.
  4. Изоляция тестов от реализации — тесты проверяют функциональность, а не конкретные селекторы.

Рекомендации по использованию с Enzyme

  • Использовать mount для комплексных компонентов с жизненным циклом, shallow для юнит-тестов.
  • Разделять Page Objects по логике приложения: страницы, модальные окна, сложные виджеты.
  • Не дублировать логику в тестах; все взаимодействия через методы Page Object.
  • Поддерживать методы Page Object короткими и понятными: один метод = одно действие пользователя.
  • Комбинировать Page Object с вспомогательными функциями для проверки состояний и ожиданий.

Page Object Pattern в сочетании с Enzyme позволяет создавать тесты, которые легко читать, поддерживать и масштабировать. Ключ к эффективности — строгая инкапсуляция взаимодействия с компонентами и ясное разделение ответственности между тестами и Page Object.