Базовые классы для переиспользования кода

В автоматизированном тестировании на JavaScript с использованием Protractor структура кода играет ключевую роль. Организация тестов с применением базовых классов позволяет избежать дублирования кода, упростить поддержку и ускорить создание новых сценариев.


Основные принципы построения базовых классов

1. Разделение ответственности. Каждый класс должен отвечать за отдельный аспект приложения: работа с конкретной страницей, управление формой, обработка общих элементов интерфейса. Такой подход соответствует паттерну Page Object Model (POM), широко используемому в Protractor.

2. Переиспользуемость. Базовый класс должен содержать методы, которые могут использоваться в нескольких страницах или тестах. Например, методы для клика по элементам, ввода текста, ожидания элементов.

3. Инкапсуляция действий. Скрытие низкоуровневой работы с веб-элементами через публичные методы класса повышает читаемость тестов и упрощает их поддержку.


Структура базового класса

Базовый класс обычно включает:

  • Селекторы элементов Для элементов интерфейса используются локаторы Protractor (by.css, by.id, by.xpath). Локаторы хранятся как приватные или защищённые свойства класса.
const { element, by, browser } = require('protractor');

class BasePage {
    constructor() {
        this.loader = element(by.css('.loader'));
        this.alert = element(by.css('.alert'));
    }

    async open(url) {
        await browser.get(url);
    }

    async clickElement(el) {
        await browser.wait(() => el.isPresent(), 5000, 'Element not present');
        await el.click();
    }

    async typeText(el, text) {
        await browser.wait(() => el.isPresent(), 5000, 'Element not present');
        await el.clear();
        await el.sendKeys(text);
    }

    async waitForLoaderToDisappear() {
        await browser.wait(async () => !(await this.loader.isPresent()), 10000, 'Loader did not disappear');
    }

    async getAlertText() {
        await browser.wait(() => this.alert.isPresent(), 5000, 'Alert not present');
        return await this.alert.getText();
    }
}
  • Общие методы Методы, которые применимы к большинству страниц: открытие URL, клик по элементу, ввод текста, ожидание появления или исчезновения элементов.

  • Методы ожиданий Использование browser.wait с условием ожидаемости (ExpectedConditions) обеспечивает стабильность тестов и снижает вероятность ошибок из-за асинхронной загрузки страницы.


Наследование и расширение базового класса

Базовый класс не содержит специфики конкретной страницы, но позволяет её легко добавлять в дочерних классах.

class LoginPage extends BasePage {
    constructor() {
        super();
        this.usernameField = element(by.id('username'));
        this.passwordField = element(by.id('password'));
        this.loginButton = element(by.id('loginBtn'));
    }

    async login(username, password) {
        await this.typeText(this.usernameField, username);
        await this.typeText(this.passwordField, password);
        await this.clickElement(this.loginButton);
        await this.waitForLoaderToDisappear();
    }
}
  • LoginPage наследует все методы BasePage.
  • Методы базового класса можно использовать для любой страницы без дублирования кода.
  • Любое обновление в базовом классе автоматически применяется ко всем дочерним страницам.

Организация проекта с базовыми классами

Стандартная структура файлов выглядит так:

/tests
  login.spec.js
  dashboard.spec.js
/pages
  base.page.js
  login.page.js
  dashboard.page.js
/utils
  wait.util.js
  browser.util.js
  • /pages — классы страниц.
  • /tests — файлы с тестами.
  • /utils — вспомогательные функции (например, дополнительные ожидания, работа с браузером).

Важные рекомендации

  • Методы базового класса должны быть универсальными, не привязанными к конкретной логике теста.
  • Локаторы элементов не должны дублироваться в дочерних классах.
  • Для сложных ожиданий рекомендуется использовать отдельные утилитарные методы или классы, чтобы базовый класс оставался лёгким.
  • Применение async/await позволяет синхронизировать действия и предотвращает неожиданные ошибки из-за асинхронного поведения браузера.

Примеры переиспользуемых методов

  • Клик по кнопке с ожиданием видимости:
async clickVisible(el) {
    const EC = protractor.ExpectedConditions;
    await browser.wait(EC.visibilityOf(el), 5000);
    await el.click();
}
  • Получение текста элемента с проверкой существования:
async getText(el) {
    await browser.wait(() => el.isPresent(), 5000);
    return await el.getText();
}
  • Выбор из выпадающего списка:
async selectOption(el, value) {
    await el.element(by.cssContainingText('option', value)).click();
}

Использование таких методов во всех страницах позволяет стандартизировать работу с элементами и снизить количество потенциальных ошибок.


Базовые классы в Protractor создают фундамент для устойчивого и масштабируемого тестирования. Грамотно спроектированные методы и структура обеспечивают переиспользуемость кода, упрощают поддержку и делают тесты читаемыми и предсказуемыми.