Page Object — это архитектурный паттерн в автоматизации тестирования, позволяющий абстрагировать работу с интерфейсом веб-приложения и сделать тесты более поддерживаемыми и читаемыми. Основная идея заключается в создании классов или модулей, которые представляют отдельные страницы или компоненты приложения и инкапсулируют все взаимодействия с ними.
1. Инкапсуляция элементов и действий Каждая страница или компонент веб-приложения оформляется как отдельный объект, содержащий:
selectors);actions), например,
ввод текста, клики, проверка состояния;getters).Такой подход минимизирует дублирование кода, так как локаторы и действия хранятся в одном месте и могут использоваться во многих тестах.
Пример:
class LoginPage {
get usernameInput() { return $('#username'); }
get passwordInput() { return $('#password'); }
get loginButton() { return $('#login'); }
async login(username, password) {
await this.usernameInput.setValue(username);
await this.passwordInput.setValue(password);
await this.loginButton.click();
}
}
module.exports = new LoginPage();
2. Четкое разделение тестов и логики страниц Тесты должны содержать только сценарии и проверки, а не детали работы с элементами. Page Object берёт на себя все низкоуровневые взаимодействия, обеспечивая:
Пример использования Page Object в тесте:
const loginPage = require('../pageobjects/login.page');
describe('Авторизация', () => {
it('позволяет пользователю войти в систему', async () => {
await browser.url('/login');
await loginPage.login('user1', 'password123');
await expect(browser).toHaveUrlContaining('/dashboard');
});
});
3. Методы высокого уровня вместо прямого взаимодействия с элементами Page Object не должен быть просто набором геттеров для локаторов. Рекомендуется создавать методы, которые отражают бизнес-действия пользователя, а не технические шаги. Это упрощает чтение тестов и снижает количество изменений при изменении интерфейса.
Плохой пример:
await $('#username').setValue('user');
await $('#password').setValue('pass');
await $('#login').click();
Хороший пример с Page Object:
await loginPage.login('user', 'pass');
4. Использование явных ожиданий и стабильных стратегий взаимодействия Page Object может включать методы для ожидания элементов перед действием. Это повышает стабильность тестов и снижает вероятность ошибок из-за асинхронной загрузки страницы.
async login(username, password) {
await this.usernameInput.waitForDisplayed();
await this.usernameInput.setValue(username);
await this.passwordInput.setValue(password);
await this.loginButton.waitForClickable();
await this.loginButton.click();
}
5. Поддержка переиспользуемости через базовые классы Часто создают базовый Page Object, который содержит общие методы для всех страниц, например:
open(path) — открыть URL страницы;getTitle() — получить заголовок страницы;waitForElement(selector) — универсальное ожидание
элемента.class BasePage {
async open(path) {
await browser.url(path);
}
async getTitle() {
return browser.getTitle();
}
async waitForElement(element) {
await element.waitForDisplayed();
}
}
module.exports = BasePage;
Дочерние страницы наследуют этот класс и расширяют функциональность специфическими методами.
6. Разделение страниц и компонентов Для крупных приложений полезно делить UI на:
Это позволяет переиспользовать код компонентов на разных страницах, сохраняя структуру чистой и модульной.
Пример компонента:
class HeaderComponent {
get profileButton() { return $('#profile'); }
get logoutButton() { return $('#logout'); }
async logout() {
await this.profileButton.click();
await this.logoutButton.click();
}
}
module.exports = new HeaderComponent();
7. Чистая организация файлов Обычно структура проекта с Page Object выглядит так:
/test
/pageobjects
login.page.js
dashboard.page.js
/components
header.component.js
footer.component.js
/specs
login.spec.js
dashboard.spec.js
Такое разделение облегчает навигацию и поддержку кода, а также повышает читаемость тестов.
8. Использование TypeScript или JSDoc для автодополнения Для крупных проектов рекомендуется добавлять аннотации типов, чтобы IDE подсказывала методы и свойства страниц. Это снижает количество ошибок и ускоряет разработку тестов.
/**
* @typedef {import('webdriverio').Element} Element
*/
class LoginPage {
/** @type {Element} */
get usernameInput() { return $('#username'); }
}
Page Object паттерн является ключевым инструментом для создания стабильных, читаемых и масштабируемых тестов в WebdriverIO. Следование его принципам обеспечивает четкое разделение ответственности, переиспользуемость кода и минимизацию влияния изменений UI на тестовую инфраструктуру.