Одним из ключевых факторов повышения стабильности и производительности автоматизированных тестов в Protractor является минимизация прямых запросов к DOM. Частые обращения к элементам страницы замедляют выполнение тестов, увеличивают вероятность флейковости и делают код трудночитаемым.
Protractor работает поверх WebDriverJS и использует промисы для
асинхронного взаимодействия с браузером. Каждый вызов методов вроде
element() или $$() инициирует запрос к DOM
через WebDriver. Частое дублирование этих вызовов приводит к
многократным обращением к одним и тем же элементам.
Пример неправильного подхода:
it('Проверка заголовка', async () => {
expect(await element(by.css('h1')).getText()).toEqual('Главная страница');
await element(by.css('h1')).click();
});
В этом примере выполняется два запроса к одному и тому же элементу — сначала для получения текста, затем для клика.
Чтобы избежать повторных запросов к DOM, элементы следует сохранять в переменные.
Пример правильного подхода:
it('Проверка заголовка', async () => {
const header = element(by.css('h1'));
expect(await header.getText()).toEqual('Главная страница');
await header.click();
});
Такой подход позволяет:
При работе с коллекциями элементов (element.all) также
важно минимизировать количество обращений.
Неправильный вариант:
const items = element.all(by.css('.item'));
await expect(await items.count()).toBe(5);
await items.get(0).click();
await items.get(1).click();
Каждый вызов get() делает новый запрос к DOM.
Оптимизированный вариант:
const items = await element.all(by.css('.item')).asElementFinders_();
expect(items.length).toBe(5);
await items[0].click();
await items[1].click();
Использование asElementFinders_() кэширует элементы,
позволяя многократно использовать их без новых запросов к DOM.
Для сложных форм и таблиц полезно сохранять состояние элементов, чтобы не перебирать их повторно.
Пример:
const tableRows = await element.all(by.css('table tr')).asElementFinders_();
for (let i = 0; i < tableRows.length; i++) {
const cells = await tableRows[i].all(by.css('td')).asElementFinders_();
console.log(await cells[0].getText());
}
Здесь коллекции tableRows и cells
кэшируются, что предотвращает повторные обращения к DOM для каждой
итерации.
Для часто используемых элементов лучше создавать локальные переменные
внутри функций или Page Object.
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(user, pass) {
await this.usernameInput.sendKeys(user);
await this.passwordInput.sendKeys(pass);
await this.loginButton.click();
}
}
Page Object позволяет:
browser.executeScript и минимизация
getTextЧастые вызовы getText() и других методов WebDriver
инициируют сетевые запросы к браузеру. Иногда более эффективным способом
является использование browser.executeScript для получения
данных напрямую через JavaScript.
Пример:
const headerText = await browser.executeScript(
"return document.querySelector('h1').textContent;"
);
expect(headerText).toEqual('Главная страница');
Этот подход уменьшает накладные расходы WebDriver и ускоряет тест.
Не стоит хранить старые ссылки на элементы, которые могут быть перерендерены динамическими фреймворками (Angular, React). В таких случаях нужно либо обновлять переменные, либо использовать локаторы в момент необходимости.
Пример безопасного подхода:
const getHeader = () => element(by.css('h1'));
await expect(await getHeader().getText()).toEqual('Главная страница');
await getHeader().click();
Функция getHeader() гарантирует актуальность локатора
без дублирования DOM-запросов.
element() и
element.all() внутри циклов.asElementFinders_() или map() для
предварительной выборки.browser.executeScript
для извлечения данных напрямую.Эти практики значительно снижают количество обращений к DOM, повышают стабильность и сокращают время выполнения тестов в Protractor.