Page Object — это шаблон проектирования для тестирования веб-приложений, который позволяет отделить логику тестов от структуры страниц, делая код более поддерживаемым и читаемым. В крупных проектах часто возникает необходимость переиспользовать общие элементы и методы между разными страницами. Для этого используется наследование Page Objects.
Повторное использование кода Часто несколько страниц содержат одинаковые элементы: меню, кнопки навигации, футеры. Вместо копирования локаторов и методов в каждый Page Object, их можно вынести в базовый класс.
Разделение ответственности Базовый Page Object отвечает за общие элементы и действия, а дочерние классы — за специфические для конкретной страницы функции.
Упрощение поддержки Любое изменение в базовом элементе или методе автоматически отражается на всех страницах-наследниках.
Базовый класс обычно содержит элементы и методы, общие для всех страниц приложения:
class BasePage {
constructor() {
this.header = element(by.css('header'));
this.footer = element(by.css('footer'));
this.navigationMenu = element(by.css('.nav-menu'));
}
async goTo(url) {
await browser.get(url);
}
async clickNavigationItem(itemText) {
const item = this.navigationMenu.element(by.xpath(`.//a[text()='${itemText}']`));
await item.click();
}
async isHeaderDisplayed() {
return await this.header.isDisplayed();
}
}
Ключевые моменты:
constructor хранит локаторы общих элементов.await, что соответствует
подходу Protractor с async/await.Дочерний класс наследует методы и элементы базового класса и добавляет специфические функции:
class LoginPage extends BasePage {
constructor() {
super();
this.usernameInput = element(by.id('username'));
this.passwordInput = element(by.id('password'));
this.loginButton = element(by.buttonText('Login'));
}
async login(username, password) {
await this.usernameInput.sendKeys(username);
await this.passwordInput.sendKeys(password);
await this.loginButton.click();
}
async isLoginButtonEnabled() {
return await this.loginButton.isEnabled();
}
}
Особенности наследования:
super() вызывает конструктор базового класса.goTo(), clickNavigationItem(),
isHeaderDisplayed().Иногда требуется изменить поведение базового метода для конкретной страницы:
class DashboardPage extends BasePage {
async clickNavigationItem(itemText) {
console.log(`Переопределён метод для DashboardPage: ${itemText}`);
await super.clickNavigationItem(itemText);
await browser.sleep(500); // дополнительная задержка для динамических элементов
}
}
Важно:
super.methodName() вызывает метод базового класса,
сохраняя основную функциональность.В больших проектах можно создавать цепочку наследования:
class BasePage { /* общий функционал */ }
class AuthPage extends BasePage { /* общие элементы для страниц авторизации */ }
class LoginPage extends AuthPage { /* специфичные элементы для LoginPage */ }
class RegistrationPage extends AuthPage { /* элементы для регистрации */ }
Такой подход позволяет максимально структурировать проект, минимизируя повторение кода и улучшая поддержку.
describe('Login tests', () => {
const loginPage = new LoginPage();
it('should log in successfully', async () => {
await loginPage.goTo('/login');
await loginPage.login('user1', 'password123');
expect(await loginPage.isHeaderDisplayed()).toBe(true);
});
});
describe('Dashboard tests', () => {
const dashboardPage = new DashboardPage();
it('should navigate to reports', async () => {
await dashboardPage.goTo('/dashboard');
await dashboardPage.clickNavigationItem('Reports');
});
});
Наследование Page Objects превращает тестовую архитектуру в гибкую и масштабируемую систему, особенно полезную для крупных и динамичных веб-приложений.