Как ускорить тесты Selenium — полный гайд

Если коротко, чтобы ускорить тесты в «Селениуме», грамотный тестировщик делает следующее:

  • продуманно выбираются wait-ожидания;
  • и локаторы;
  • меняются настройки и дизайн тестов;
  • применяются продуманные подходы (стратегии);
  • а также оптимизируется тестовая инфраструктура (переход в облако).

Теперь подробнее.

Стандартный тест

Стандартный тест в Selenium умеет, например:

  1. Открыть URL тестируемой страницы (через WebDriver / Remote WebDriver).
  2. Найти нужные элементы с помощью локаторов (XPath, CssSelector, LinkText).
  3. Выполнить действия с элементами.
  4. Освободить ресурсы, с которыми работал WebDriver.

Скорость выполнения зависит от структуры теста, его методов. 

  • Следует иметь в виду, то обслуживание тестов иногда усложняется с выходом крупных обновлений Selenium. 

Правильные веб-локаторы

Веб-локаторы Selenium это узловая вещь тестового сценария. В процессе автоматизации мы находим элемент нужным веб-локатором, и выполняем действие.

Применяются следующие веб-локаторы Selenium:

  • XPath
  • CSS Selector
  • Name
  • LinkText
  • Partial LinkText
  • TagName
  • ClassName

Веб-локаторы сочетаются с методом find_element (или find_elements).

Какой локатор самый быстрый?

В терминах скорости, самый быстро работающий — ID; поскольку ID-локатор в WebDriver уникален для каждого элемента страницы. ID-локатор возвращает веб-элемент (WebElement), соответствующий указанному значению (или строке). Если на странице найдено несколько элементов с одинаковым ID, то document.getElementById() возвращает первый найденный из них.

Популярные веб-браузеры оптимизируют работу метода document.getElementById(), что дает некоторое ускорение поиска WebElement в DOM.

Если WebElement не имеет атрибута ID, рекомендуется работать через атрибут NAME. Если нет ни ID, ни NAME-атрибута, придется работать с локатором CSS Selector. CSS Engine почти одинаковый во всех распространенных браузерах, и их скорость оптимизирована для связки CSS Selectors + Selenium. Это значит, что не предвидится особых проблем браузерной совместимости с этим веб-локатором. 

CSS Selector в целом находит элементы быстро, и тесты выполняются за небольшой интервал. CSS Selector лучше подходит для устаревших версий и браузеров (типа Internet Explorer); а также, лучше читается код чисто визуально, по сравнению с XPath.

XPath — самый “тормозной” веб-локатор, с ним случаются проблемы на разных браузерах. Рекомендуется применять XPath только если нет другого варианта.

Итак, выстроим веб-локаторы по желательности применения:

  • ID
  • Name
  • CSS Selector
  • XPath

Меньше веб-локаторов

Следующий момент: снизить количество применяемых веб-локаторов, по возможности. 

Каждый запрос метода find_element(By) “нагружает” DOM-дерево. Чем больше запросов, тем дольше выполняется скрипт. Лучшая практика состоит в ограничении количества таких запросов, (особенно если скорость критична). Да и сами скрипты будут лучше читаться (то есть лучше обслуживаться).

Избегай Thread.sleep()

Контент сайта (приложения) может быть как статическим так и динамическим. Сейчас везде используется AJAX (Asynchronous JavaScript And XML) для динамической подгрузки контента. Элементы страницы могут загружаться с разным интервалом; что создает трудности при операциях с элементами которых пока нет в DOM.

Рекомендуется проверять состояние DOM, путем мониторинга статуса document.readyState. Если document.readyState в “завершенном” состоянии, то все ресурсы страницы считаются загруженными, и доступными для операций. 

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

В Selenium существует несколько способов задания ожиданий. Из них Thread.sleep() — надо избегать в любом случае. (Этот метод приостанавливает выполнение кода на указанное количество миллисекунд):

/* Pauses test execution for specified time in milliseconds */
Thread.sleep(5000);

В этом коде ожидание составляет 5 секунд (то есть 5000 миллисекунд). А если все элементы уже загрузились? (например, за 2 секунды) Тогда 3 секунды — попусту потраченное время. Скорость загрузки зависит от многих вещей (нагрузка на сервер, особенности дизайна страницы, кеширование, тип сети), поэтому тяжело точно знать время загрузки наперед. Хорошая практика автоматизации Selenium: точнее оценивать время загрузки при автоматизации.

Данный метод ожидания Thread.sleep() с фиксированным временем — плохая практика, практически для любой страницы.

Повторное использование экземпляра браузера

Все фреймворки автоматизации, работающие в связке с Selenium, поддерживают Аннотации, ускоряющие написание и выполнение тестов (особенно с разными входными данными). Тут важно выбрать правильную аннотацию:

ФреймворкАннотации
JUnit [Selenium Java]@BeforeClass, @Before, @Test, @After, @AfterClass, @Ignore
TestNG [Selenium Java]@BeforeSuite, @BeforeTest, @BeforeClass, @BeforeMethod, @Test, @AfterMethod, @AfterClass, @AfterTest, etc.
NUnit [Selenium C#][SetUp], [TearDown], [Test], [TestCase], [OneTimeSetUp], [OneTimeTearDown], [Ignore], [Category], etc.
XUnit [Selenium C#][Theory], [InlineData], [Fact], etc.

Есть сценарии, в которых запускается один тест (или группа тестов) в одном браузере / в одной ОС. Тогда создание нового экземпляра WebDriver при запуске каждого теста замедляет процесс.

JUnit + Selenium

Порядок выполнения аннотаций в JUnit for Selenium:

В автотесте Selenium + JUnit, экземпляр Selenium WebDriver создается методом SetUp, имплементированном аннотацией @Before. Созданный экземпляр удаляется методом TearDown, имплементированном аннотацией @After.

TestNG + Selenium

Порядок выполнения аннотаций в TestNG + Selenium:

Аналогично как с JUnit, SetUp-метод имплементируется аннотацией @BeforeMethod, и TearDown-метод — аннотацией @AfterMethod.

Серии тестов в комбинациях Браузер+ОС

Например, нужно выполнить три теста для комбинации браузера и ОС; тогда это выглядит следующим образом:

JUnit

Test – 1setUp() under @Before annotation -> Test-1 -> tearDown() under @After annotation
Test – 2setUp() under @Before annotation -> Test-2 -> tearDown() under @After annotation
Test – 3setUp() under @Before annotation -> Test-3 -> tearDown() under @After annotation

TestNG

Test – 1setUp() under @BeforeMethod annotation -> Test-1 -> tearDown() under @AfterMethod annotation
Test – 2setUp() under @BeforeMethod annotation -> Test-2 -> tearDown() under @AfterMethod annotation
Test – 3setUp() under @BeforeMethod annotation -> Test-3 -> tearDown() under @AfterMethod annotation

Экземпляр браузера создается и удаляется после каждого сценария.

Повторное использование (re-using) экземпляра браузера в JUnit и TestNG

Может применяться один и тот же экземпляр, с соответствующими аннотациями:

JUnit

Test – 1, Test – 2, and Test -3setUp() under @BeforeClass annotation -> Test-1, Test-2, and Test-3 -> tearDown() under @AfterClass annotation

TestNG

Test – 1, Test – 2, and Test -3setUp() under @BeforeTest (or @BeforeClass) annotation -> Test-1, Test-2, and Test-3 -> tearDown() under @AfterTest (or @AfterClass) annotation

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

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

Применение эксплицитных ожиданий в Selenium

Имплицитное wait-ожидание в Selenium применяется ко всем веб-элементам скрипта. Крупный недостаток имплицитного ожидания в том, что оно неприменимо на параметрах типа Visible, Clickable, Selectable и т.п. 

А вот эксплицитное ожидание позволяет прописывать ожидание “по условию появления” элементов страницы. Например, ElementNotVisibleException выдается, когда указанный WebElement все еще появился (не видимый) по истечению интервала, указанного в эксплицитном wait-е. Метод elementToBeClickable возвращает WebElement, если найденный элемент кликабельный.

Для эксплицитного ожидания применяется сочетание классов WebDriverWait и ExpectedConditions. Эксплицитное ожидание не ожидает конца интервала, а завершается по указанному условию. Если WebElement есть, он возвращается в качестве результата. Если WebElement-а нет в DOM, выбрасывается TimeOutException, (не  глядя на время прописанное в условии).

В примере ниже — эксплицитное ожидание 5 секунд по условию visibilityOfElementLocated. Если веб-элемент с ID = “element” найден за 5 секунд, эксплицитное ожидание завершается, и возвращается нужный WebElement.

/* Trigger an explicit wait of 5 Seconds */
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("element"));

В целом эксплицитное ожидание ускоряет выполнение скриптов, поскольку WebElement доступен сразу после того как найден. Эксплицитные ожидания ускоряют тесты в Selenium, потому что более гибко обрабатывают сценарии.

“Независимые” автономные скрипты

Видимо основное требование по части дизайна “селениумных” тестов. Независимо от сложности сценария, лучше разбивать сложный сценарий на несколько “независимых” тестов-кейсов.

Фреймворки автоматизации типа TestNG поддерживают объявление эксплицитных зависимостей между тестовыми методами, через аннотации — например dependsOnMethods (для методов) и dependsOnGroups (для групп). Однако, зависимости должны создаваться только если данные и состояния будут расшариваться между методами.

“Атомарные” тесты лучше при определении багов. Маленькие “атомарные” тесты экономят время на обслуживание.

“Атомарность” Selenium-тестов упрощает тесты, минимизирует проблемы при имплементации, облегчает обслуживание, и поэтому ускоряет процесс.

Параллельное тестирование

Параллельное тестирование позволяет запускать тесты одновременно в разных тестовых окружениях. Если планируется применять локальный Selenium Grid в распределенном тестировании, рекомендуется воспользоваться Selenium Grid 4, так быстрее.

В зависимости от сценариев, возможна параллелизация на уровне классов или методов. Параллелизация может быть через:

  • Группирование тестовых сценариев
  • Параметризация тестовых сценариев
  • Облачный Selenium Grid

Группы тестовых сценариев

Сложность тестового набора растет с добавлением в него новых файлов и методов. Для минимизации проблем с имплементацией и обслуживанием, следует группировать тесты, по их функциональности.

Как показано на примере группирования тест-кейсов в TestNG, создаются две группы тестов (Search и ToDo), и идет параллельное выполнение, на уровне методов. Атрибут thread-count в TestNG прописывает параллельное выполнение методов, с указанным максимальным числом потоков — 4.

  • Группирование тестовых сценариев уменьшает сложности обслуживания и ускоряет выполнение.

Selenium 4 — лучше чем Selenium 3

QA-комьюнити ждет официального релиза “Четвертого Селениума”, а пока есть бета. Заметны продвижения:

  • Оптимизированный-улучшенный Grid
  • Стандартизация WebDriver под W3C
  • Улучшенный Selenium 4 IDE
  • Оптимизированные Chrome DevTools
  • Оптимизированные Relative Locators

Все это должно ускорить тесты в Selenium.

В 3-ей версии используется JSON Wired Protocol коммуникации между браузером и тестовым кодом. Это вызывает иногда проблемы с API-запросами и W3C-протоколом. В Selenium 4 используется WebDriver W3C-протокол, работающий с браузером быстрее. Кроме быстроты, заявляется улучшение стабильности тестов.

Relative Locators (бывшие friendly locators) в Selenium 4 полезны для работы с веб-элементами, “прилегающими” к какому-то веб-элементу, то есть соседними. Ниже показаны новые такие локаторы в Selenium 4:

Вместо методов find_element() и findelements() для каждого веб-элемента страницы, применяются relative locators + TagName (если это Selenium 4 Java), и запросы в DOM идут быстрее.

Могут быть и другие улучшения. Переход на 4-ю версию должен быть без проблем.

Облачный Selenium Grid

Параллельное выполнение тестов из локального Selenium Grid имеет серьезные недостатки, если идет речь о масштабируемости и надежности. Не подходит для больших веб-приложений, где много тест-сьютов запускаются на множестве браузеров, ОС и устройств.

Облачное тестирование в Selenium ускоряет процессы, поскольку параллельное выполнение выполняется более надежно, и хорошо масштабируемо. Ниже показан testng.xml; параллельное выполнение идет на уровне теста, то есть каждый <test>-тег выполняется в отдельном потоке.

Как видно выше, два теста выполняются параллельно в облаке.

Облачный Selenium Grid = ускорение тестов:

Отключение изображений на странице

После создания экземпляра Selenium WebDriver, применяется метод driver.get() для открытия страницы. Скорость загрузки сильно зависит от структуры страницы, и конечно большое количество изображений замедляет загрузку.

Можно отключить их программно.

Отключение изображений в Chrome при выполнении Selenium-скрипта

package com.disableimages;
 
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.testng.annotations.AfterTest;
import org.testng.annotations.Test;
import java.util.HashMap;
 
public class test_disable_image_demo
{
    String test_url = "https://www.amazon.com";
    WebDriver driver = null;
 
    @Test(enabled=true, priority = 1)
    public void test_disable_images_chrome() throws InterruptedException
    {
        ChromeOptions options =new ChromeOptions();
        HashMap<String, Object> prefs = new HashMap<String, Object>();
        prefs.put("profile.managed_default_content_settings.images", 2);
 
        options.setExperimentalOption("prefs", prefs);
        driver = new ChromeDriver(options);
 
        driver.get(test_url);
        driver.manage().window().maximize();
 
        Thread.sleep(5000);
    }
 
    @AfterTest
    public void tearDown()
    {
        if (driver != null)
        {
            driver.quit();
        }
    }
}

Выше мы отключили изображения на странице Amazon, это должно сильно ускорить загрузку страницы.

Отключение изображений в Firefox 

package com.disableimages;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.testng.annotations.AfterTest;
import org.testng.annotations.Test;
import java.util.HashMap;
 
public class test_disable_image_demo
{
    String test_url = "https://www.amazon.com";
    WebDriver driver = null;
 
    @Test(enabled=true, priority = 1)
    public void test_disable_images_firefox() throws InterruptedException
    {
        FirefoxProfile profile = new FirefoxProfile();
        profile.setPreference("permissions.default.image", 2);
 
        FirefoxOptions options = new FirefoxOptions();
        options.setProfile(profile);
        driver = new FirefoxDriver(options);
 
        driver.get(test_url);
        driver.manage().window().maximize();
 
        Thread.sleep(5000);
    }
 
    @AfterTest
    public void tearDown()
    {
        if (driver != null)
        {
            driver.quit();
        }
    }
}

Выше изображения отключены установкой параметра permissions.default.image = 2. 

Одна из хороших практик тестирования, которая недооценивается junior QA, учитывая обилие изображений на современных сайтах.

Data-Driven-тестирование и параметризация

Если надо запустить сценарий на многих комбинациях браузеров и ОС, или при разных комбинациях входных данных, hard-coding значений в тестовых методах — не лучшее решение. Лучше сделать параметризацию, особенно если в проекте большой набор данных.

Параметризация в Selenium как известно улучшает тестовое покрытие и ускоряет сами тесты. Параметризацию поддерживают все популярные фреймворки автоматизации, типа MSTest, NUnit, JUnit, TestNG и PyTest.

Например TestNG передает параметры в testng.xml, и при параллельном выполнении (на уровне тестов) значительно ускоряет процесс.

Headless-браузеры и драйверы

Для проверки действий с UI-элементами вызывается драйвер в non-headless-режиме.

Headless-браузер запускает UI-тесты без GUI. Такое тестирование ускоряет кроссбраузерные тесты — они выполняются в фоновом режиме. Популярные браузеры типа Chrome и Firefox могут работать в headless-режиме.

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("build", "your build name");
capabilities.setCapability("name", "your test name");
capabilities.setCapability("platform", "Windows 10");
capabilities.setCapability("browserName", "Chrome");
capabilities.setCapability("version","89.0");
capabilities.setCapability("headless",true);

Headless-тесты это она из лучших практик в Selenium, которая должна применяться, если не нужно проверять UI-действия. 

Другие (возможно даже более удобные) headless-браузеры:

  • HTMLUnit
  • Splash
  • PhantomJS
  • TrifleJS
  • ZombieJS
  • SimpleBrowser

HTMLUnitDriver на основе HTMLUnit — это упрощенная имплементация headless-браузера под Selenium WebDriver. HTMLUnitDriver похож на драйверы Chrome, Firefox, кроме того что нет GUI. 

Браузер HTMLUnit написан на Java, поддерживает AJAX и JavaScript. Есть, частично, функции рендеринга. Ниже показан сниппет (на Selenium Java) создания экземпляра HTMLUnitDriver и запуска в Selenium:

import org.openqa.selenium.By;      
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;  
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
 
/* Create a new instance of the HtmlUnitDriver */
WebDriver driver = new HtmlUnitDriver();
 
/* Perform necessary actions as per the desired test requirement */
................
................
 
/* Release the resources held by HtmlUnitDriver */
driver.quit();

PhantomJS — другой популярный headless-браузер, работающий через JavaScript API. Для бекенда здесь QtWebKit, нативно поддерживаются стандарты JSON, Canvas, SVG, обработка DOM и другое. Интегрированный GhostDriver. Браузер доступен для Windows, Linux и MacOS X. Годен для:

  • Headless-тестирования
  • Автоматизации 
  • Захвата экрана
  • Мониторинга сети

Ниже — сниппет (Selenium Java) создания PhantomJS Driver и и выполнения браузерных тестов в Selenium:

import org.openqa.selenium.By;      
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;  
import org.openqa.selenium.phantomjs.PhantomJSDriver;
 
/* Create a new instance of the PhantomJS Driver*/
WebDriver driver = new PhantomJSDriver();
/* Perform necessary actions as per the desired test requirement */
................
................
 
/* Release the resources held by PhantomJS Driver */
driver.quit();

Headless-тестирование должно применяться для ускорения тестов в Selenium. Кроме приведенных HtmlUnitDriver и PhantomJSDriver, можно (и нужно) использовать headless-версии Chrome и Firefox.

Итого

Важно всегда иметь в виду продуктивность тестов, главным показателем которой является скорость. Предпочтительный путь: параллельное выполнение + облачный Selenium Grid. Как видим выше, существует много практик ускорения тестов; быстрые тесты это довольные тестировщики, хорошее покрытие, и в конечном итоге приемлемое качество продукта.

источник

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас