В тестировании с использованием Protractor Page Objects выполняют роль абстракции над элементами интерфейса, инкапсулируя взаимодействия с ними. Кэширование этих объектов позволяет повысить производительность тестов, снизить количество повторяющихся поисков элементов в DOM и обеспечить более стабильное поведение при асинхронных действиях.
При работе с Protractor каждый вызов метода
element(locator) инициирует поиск элемента в DOM. Если один
и тот же элемент используется многократно, это приводит к:
Кэширование позволяет хранить ссылку на найденный элемент или коллекцию элементов, чтобы не выполнять повторный поиск, особенно при многоступенчатых тестах с частым взаимодействием с одними и теми же элементами.
Одиночные элементы обычно кэшируются как свойства класса Page Object. Например:
class LoginPage {
constructor() {
this.usernameInput = element(by.id('username'));
this.passwordInput = element(by.id('password'));
this.loginButton = element(by.css('.login-btn'));
}
async login(username, password) {
await this.usernameInput.sendKeys(username);
await this.passwordInput.sendKeys(password);
await this.loginButton.click();
}
}
Особенности:
Иногда элемент может появляться не сразу. В этом случае кэширование лучше делать ленивым, с отложенным поиском элемента:
class DashboardPage {
get welcomeMessage() {
if (!this._welcomeMessage) {
this._welcomeMessage = element(by.css('.welcome-msg'));
}
return this._welcomeMessage;
}
}
Плюсы ленивого подхода:
Минусы:
Если тест взаимодействует с набором элементов, например списком товаров, кэшировать лучше не сами элементы, а локатор, а получение элементов делать через метод с кэшированием:
class ProductsPage {
constructor() {
this._productItems = null;
}
async getProductItems() {
if (!this._productItems) {
this._productItems = element.all(by.css('.product-item'));
}
return this._productItems;
}
}
Особенности:
element.all() возвращает объект коллекции, а не
массив.get, first,
last) выполняют поиск при каждом обращении.Актуальность элементов Protractor работает с
динамическим Angular-приложением, где DOM может обновляться после
действий пользователя. Закэшированный элемент может потерять
актуальность. Рекомендуется использовать ExpectedConditions
или переинициализировать элемент при необходимости.
Использование асинхронности Все взаимодействия с
элементами через Protractor возвращают Promise. При
кэшировании стоит избегать хранения результата промиса, а хранить
сам локатор:
// Плохо:
this.cachedText = element(by.css('.title')).getText(); // сохраняет промис, а не элемент
// Хорошо:
this.titleElement = element(by.css('.title')); // сохраняет элемент, промис вызывается при обращенииОбновление кэша При изменении страницы или переходе между вкладками стоит пересоздавать Page Object или очищать кэш:
class SomePage {
resetCache() {
this._someElement = null;
this._someCollection = null;
}
}Сбалансированное использование кэша Кэширование ускоряет тесты, но чрезмерное хранение элементов может привести к проблемам с памятью или устаревшим ссылкам на DOM. Для динамических элементов предпочтительно ленивое получение и частичная очистка кэша.
ExpectedConditions для проверки наличия
элементов перед взаимодействием, чтобы избежать ошибок stale
element.class UserProfilePage {
constructor() {
this.header = element(by.css('.profile-header')); // статичный элемент
this._notifications = null; // ленивое кэширование
}
get notifications() {
if (!this._notifications) {
this._notifications = element.all(by.css('.notification-item'));
}
return this._notifications;
}
async waitForHeader() {
const EC = protractor.ExpectedConditions;
await browser.wait(EC.visibilityOf(this.header), 5000);
}
resetCache() {
this._notifications = null;
}
}
Такой подход обеспечивает оптимизацию производительности, устойчивость тестов и гибкость при работе с динамическим интерфейсом.