Данная статья будет полезна начинающим QA специалистам, а также тем, кто интересуется особенностями и возможностями таких популярных фреймворков тестирования, как Selenide и Selenoid.
Здесь мы рассмотрим некий базовый проект на Selenium. Увидим, как подключить Selenium и TestNG к проекту, пример Page Object с описанием элементов страницы и используемых методов.
Далее, знакомство с Selenide: рассмотрим сам фреймворк, его основные возможности и преимущества, добавление Selenide в тестовый проект. Рассмотрим работу с элементами, проверки и ожидания, доступные в Selenide.
И наконец, подключим к своему проекту фреймворк Selenoid для запуска тестов в Docker контейнере и вне его.

* Статья подготовлена на основе доклада Никиты Калиниченко — Senior QA специалиста компании IntexSoft.
В статье присутствуют ссылки на внешние материалы.
1. Selenium + TestNG
Мы рассматриваем проект на сборщике Maven, поэтому описание структуры проекта мы можем найти в файле pom.xml. Для того, чтобы использовать Selenium и TestNG, в наш файл pom.xml должны быть добавлены соответствующие зависимости. Их вы можете наблюдать между тегами dependencies ниже:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test</groupId> <artifactId>test</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.141.59</version> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.14.3</version> <scope>test</scope> </dependency> </dependencies> </project>
Далее мы рассмотрим пример Page Object’а:
import... public class SignUpPage { private WebDriver driver; public SignUpPage(WebDriver driver) { this.driver = driver; } private By emailField = cssSelector("#register-email"); private By confirmEmailField = cssSelector("#register-confirm-email"); private By passwordField = cssSelector("#register-password"); private By displayNameField = cssSelector("#register-displayname"); private By monthDropDown = cssSelector("#register-dob-month"); private By dayField = cssSelector("#register-dob-day"); private By yearField = cssSelector("#register-dob-year"); private By shareCheckbox = cssSelector("#register-thirdparty"); private By registerButton = cssSelector("#register-button-email-submit"); //Метод для заполнения поля email public SignUpPage typeEmail(String email) { //Находим поле и вводим в него текст driver.findElement(emailField).sendKeys(email); return this; } //Метод для заполнения поля Confirm email public SignUpPage typeConfirmEmailField(String email) {...} //Метод для заполнения поля password public SignUpPage typePassword(String password) {...} //Метод для заполнения поля ввода имени public SignUpPage typeName(String name) {...} //Метод выбора месяца public SignUpPage setMonth(String month) {...} //Метод для заполнения поля Day public SignUpPage typeDay(String day) {...} //Метод для заполнения поля Year public SignUpPage typeYear(String year) {...}
Как мы видим, в верхней части java-файла находится описание переменных с локаторами для элементов страницы регистрации. Ниже секции переменных расположены методы непосредственно для взаимодействия с элементами нашей страницы.
Откроем сами тесты:
//Добавляем переменную страницы регистрации private WebDriver driver; //Добавляем переменные драйвера private SignUpPage page; @BeforeMethod public void setUp() { //Указываем путь где хранится geckodriver System.setProperty("webdriver.gecko.driver", "C:\\Users\\Nikita\\IdeaProjects\\autotests_examples\\drivers\\geckodriver.exe"); //Создаем новый драйвер driver = new FirefoxDriver(); //Добавляем неявное ожидание, используемое при поиске любого из элементов driver.manage().timeouts().impicitlyWait(10, TimeUnit.SECONDS); //Открываем url в браузере с помощью метода webdrivera get driver.get("https://www.spotify.com/us/signup/"); }
Как видим, в аннотации BeforeMethod мы описываем то, что у нас будет происходить перед каждым методом.
@Test public void typeInvalidYear() { //Создаём объект класса страницы регистрации и передаём в конструктор класса driver page = new SignUpPage(driver); //Устанавливаем месяц page.setMonth("December"); //Указываем день .typeDay("20") //Указываем год .typeYear("85") //Жмём чекбокс .setShare(true); //Проверяем видимость ошибки, ошибка должна быть видна; Assert.assertTrue(page.isErrorVisible("Please enter a valid year."));
В аннотации Test приведён код тестовых методов.
@AfterMethod //Метод, закрывающий браузер public void tearDown() { driver.quit(); }
А в аннотации AfterMethod содержится код, который должен выполниться после каждого метода.
При запуске тестов с использованием Selenium будет происходить следующее:
- Открытие отдельного окна браузера
- Переход по url
- Выполнение кода тестов
- Закрытие сессии и окна браузера после каждого теста
При выполнении следующего теста будет происходить то же самое. Следует упомянуть, что выполнение тестов на Selenium – довольно ресурсозатратный процесс.
2. Selenide: что, где, и как
Что же такое сам Selenide, каковы его основные возможности и преимущества?
Если кратко, Selenide – это обёртка вокруг Selenium WebDriver, позволяющая быстро и просто его использовать при написании тестов. По своей сути, Selenide – это инструмент для автоматизации действия пользователя в браузере, ориентированный на удобство и легкость реализации бизнес-логики в автотестах на языке пользователя, не отвлекаясь на технические детали работы с «драйвером браузера». Для примера, нам не нужно акцентировать внимание на работе с ожиданиями элементов в процессе автоматизации тестирования динамических веб-приложений, а также на реализации высокоуровневых действий над элементами.
Ключевые и главные преимущества Selenide:
- Лаконичный синтаксис в духе jQuery
- Автоматическое решение большинства проблем с Ajax, ожиданиями и таймаутами
- Управление жизнедеятельностью браузера
- Автоматическое создание скриншотов
Цель Selenide – сосредоточиться на бизнес-логике тестов и не “растрачивать” ментальную энергию на технические детали.
Перевод проекта на Selenide
Для того, чтобы подключить Selenide и начать работу с ним, в файле pom.xml между тегами dependencies мы указываем зависимость от Selenide. Так как зависимость Selenium нам больше не нужна, мы попросту ее удаляем.
<dependency> <groupId>com.codeborne</groupId> <artifactId>selenide</artifactId> <version>5.2.8</version> </dependency>
Далее, для того, чтобы подключить и начать использовать Selenide в своем проекте, нам необходимо сделать несколько импортов. Примеры импортов:
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Selectors.*;
import static com.codeborne.selenide.Condition.*;
import static com.codeborne.selenide.CollectionCondition.*;
Подробнее о том, как подключить Selenide, используя остальные сборщики проектов, можно прочесть в разделе Quick Start документации Selenide.
Работа с элементами, проверки и ожидания
Перейдем к рассмотрению работы Selenide с элементами и познакомимся с некоторыми проверками и ожиданиями, доступными нам в Selenide.
import... public class SignUpTest { //Добавляем переменную страницы регистрации private SignUpPage page; @BeforeClass public static void setUp() { //Property baseurl, которое хранится в классе Configuration и будет являться базовым url baseurl = "https://www.spotify.com"; //Property browser, которое находится в классе Configuration и указывает на каком браузере будет выполнен запуск тестов browser = "chrome"; }
В файле с тестами мы заменяем аннотацию BeforeMethod на аннотацию BeforeClass, так как она нам больше не нужна, Selenide нас избавляет от нужды писать Before и After методы – функцию AfterMethod на себя берет сам Selenide. У нас остаётся только аннотация BeforeClass чтобы прописать пару properties.
В аннотации BeforeClass мы прописали property baseurl, которое находится в классе configuration и будет являться базовым url. Поэтому, driver.get, который мы использовали в тестах на Selenium, больше не нужен. В property browser мы прописываем браузер, на котором мы будем осуществлять запуск наших тестов.
В своём тестовом проекте мы можем полностью отказаться от драйвера Selenium, всю работу с ним Selenide возьмёт на себя, инкапсулируя её в своих классах. Нам останется сосредоточиться на логике самих тестов.
Перейдем к использованию Selenide на нашей странице:
//Метод для открытия страницы с помощью метода Selenide open public SignUpPage open() { //Указываем обратный путь Selenide.open (relativeOrAbsoluteUrl: "/us/signup/"); return this; } //Метод для заполнения поля email public SignUpPage typeEmail(String email) { //Находим поле и вводим в него текст $(emailField).sendKeys(email); return this; }
При вызове метода open, Selenide сам запускает браузер и открывает страницу. Также он заботится о том, чтобы в конце браузер закрылся. В Selenide.open мы можем прописать либо полный путь c http, либо передать какой-либо относительный к baseurl – relative url. В качестве baseurl мы указали абсолютный путь, поэтому в методе Selenide.open нам достаточно будет указать “/”.
//Метод для заполнения поля email public SignUpPage typeEmail(String email) { //Находим поле и вводим в него текст $(emailField).sendKeys(email); return this; } //Метод для заполнения поля Confirm email public SignUpPage typeConfirmEmailField(String email) { //Находим поле и вводим в него текст $(confirmEmailField).setValue(email); return this; }
Для того, чтобы с использованием Selenide найти элемент, нам необходимо указать $ вместо driver.findElement, который используется в Selenium. То есть, при помощи метода длиной в один символ, мы можем найти непосредственно сам элемент. По умолчанию, метод поиска принимается в виде строки, аналогично библиотеке JavaScript jQuery.
Для того, чтобы с использованием Selenide найти список элементов, нужно указать два символа $$. Вместо List<WebElement, мы прописываем ElementsCollection, которая уже расширена дополнительными методами.
Для работы с элементами мы можем использовать как стандартные методы Selenium (sendKeys()), так и setValue() или более короткую версию vаl().
Исходя из наименования, как мы видим, методы Selenide более понятны. Метод click() таким и остается. Хотя у Selenide есть несколько методов click(): contextClick() (имитация нажатия правой кнопки мыши), doubleClick() (имитация двойного нажатия по элементу) и т.д. Имея уже найденный элемент мы можем продолжать поиск при помощи других локаторов.
От метода Selenium driver.findElement(By), метод Selenide find() отличается тем, что сразу умеет получать CSS селекторы и оперирует c Selenide-элементами, а не с Web-элементами. В принципе, Selenide-элементы – это более умная и со своими методами альтернатива Web-элементам Selenium.
Selenide уже содержит в себе методы, которые пришлось бы делать посредством какого-либо action класса или ещё каким-то образом. Он позволяет писать краткие и “красивые” методы, написанные понятным всем языком. Также, Selenide обладает большой долей гибкости, благодаря которой мы можем использовать стандартные возможности Selenium.
С остальными методами Selenide также можно ознакомится в официальной документации.
Рассмотрим широкие и понятные примеры проверок, которые нам предоставляет Selenide:
//Проверяем видимость ошибки, ошибка должна быть видна page.getError("Please enter a valid year.").shouldBe(Condition.visible); //Проверяем видимость ошибки, ошибка должна быть не видна page.getError("When were you born?").shouldNotBe(Condition.visible); //Проверяем количество ошибок сравнивая с размером списка page.getErrors().shouldHave(CollectionCondition.size(6)); //Проверяем текст ошибки по её номеру page.getErrorByNumber(3).shouldHave(Condition.text("Please enter your birth month."));
Схема проверки Selenide позволяет нам взять некий элемент, найти его и использовать к нему следующие формулировки: should, shouldBe, shouldHave, shouldNot, shouldNotBe и shouldNotHave. Схема проверки сводится к нахождению элемента и вызовом у него этих формулировок. Далее в скобках мы указываем, либо состояние, которому он должен соответствовать или не соответствовать, либо какой-либо атрибут.
В зависимости от логики и наших потребностей, мы используем определенные методы. Если мы хотим проверить, что элемент существует, мы используем метод should(exist), если мы хотим проверить видимость элемента, мы используем shouldBe(visible) и т.д. По сути мы используем только три формулировки: либо should, shouldBe, shouldHave, либо обратные им shouldNot, shouldNotBe, shouldNotHave.
Проверки над элементами или коллекциями элементов совершаются на Selenide с помощью описанных выше методов, которым передается условие для проверки. Они играют роль ожидания момента, когда элемент будет удовлетворять какому-то условию, а не только совершают проверку по условию.
Формулировки в Selenide довольно логичны и понятны. Мы можем написать наши проверки, либо используя подсказки среды разработки, либо логические предположения. И само собой, мы всегда можем взглянуть на код реализации нужных методов в документации или заглянуть в реализацию самого метода.
Автоматические скриншоты в тесте
Для JUnit:
Чтобы автоматически делать скриншот после каждого упавшего теста, можно сделать импорт и указать Rule
import com.codeborne.selenide.junit.ScreenShooter; @Rule public ScreenShooter makeScreenshotOnFailure = ScreenShooter.failedTests();
Но по сути, это рудимент, так как Selenide уже давно автоматически делает скриншоты при падении тестов. Это очень удобно для анализа наших ошибок. По умолчанию Selenide складывает скриншоты в папку build/reports/tests.
Для того, чтобы автоматически делать скриншот после каждого теста (в т.ч. зелёного), можно использовать следующую команду:
@Rule public ScreenShooter makeScreenshotOnFailure = ScreenShooter.failedTests().succeededTests();
Для TestNG мы также делаем импорт:
import com.codeborne.selenide.testng.ScreenShooter; @Listeners({ ScreenShooter.class})
Чтобы делать скриншоты после зелёных тестов, нужно вызвать следующую команду перед запуском тестов:
ScreenShooter.captureSuccessfulTests = true;
Также можно сделать скриншот в любом месте теста одной строкой:
import static com.codeborne.selenide.Selenide.screenshot; screenshot("my_file_name");
При этом Selenide создаст два файла: my_file_name.png и my_file_name.html
3. Docker: особенности и преимущества использования
Перейдем непосредственно к Docker и рассмотрим его преимущества:
- Ускоренный процесс разработки. Нет необходимости устанавливать вспомогательные инструменты их можно запускать в контейнерах
- Удобная инкапсуляция приложений
- Понятный мониторинг
- Простое масштабирование
Когда мы говорим о Docker’е, следует прояснить следующие моменты:
Контейнер – это исполняемый экземпляр, который инкапсулирует требуемое программное обеспечение. Он состоит из образов. Его можно легко удалить и снова создать за короткий промежуток времени.
Образ – базовый элемент каждого контейнера.
Docker Hub – публичный репозиторий с интерфейсом, предоставляемый Docker Inc. Он хранит множество образов. Ресурс является источником «официальных» образов, сделанных командой Docker или созданных в сотрудничестве с разработчиком ПО.
Установка Docker
Чтобы получить Docker на Windows, мы идём на https://hub.docker.com и скачиваем приложение Docker Desktop для Windows или MacOS с последующей установкой.
Для Ubuntu Linux устанавливаем Docker командой sudo apt install docker.io
Далее необходимо запустить Docker и настроить его автоматический запуск при загрузке системы выполнив следующие команды:
- sudo systemctl start docker
- sudo systemctl enable docker
4. Selenoid: возможности и преимущества
Selenoid – это сервер, запускающий изолированные браузеры в Docker контейнерах.
Преимущества использования Selenoid:
- Единая среда для параллельного запуска автотестов
- Изолированное окружение: каждый браузер запускается в отдельном контейнере, что позволяет полностью изолировать окружение нашего браузера
- Масштабируемость: окружение никак не влияет на качественное и непрерывное проведение тестов
- Потребление и утилизация ресурсов: Selenoid позволяет поддерживать высокую нагрузку без дополнительных ресурсозатрат; кроме того, он утилизирует все неактивные контейнеры после завершения самой его сессии, тем самым постоянно поддерживая нужно количество свободной памяти
- Установка: не занимает много времени и осуществляется, по сути, при помощи одной команды
- Одновременная поддержка нескольких версий одного браузера: данная опция доступна только у Selenoid, для этого необходимо создать несколько контейнеров с необходимыми браузерами
- Фокус: операционная система работает таким образом, что в фокусе может быть только одно окно. При запуске нескольких браузеров на одной машине, окна могут начать конкурировать за фокус. У Selenoid такой проблемы нет, поскольку каждый тест запускается в отдельном контейнере
- Пользовательский интерфейс и логи: Selenoid позволяет быстро получить доступ к имеющимся журналам. Помимо этого есть возможность интеграции с ELK стэком для более быстрого сбора и анализа текущих файлов.
Также, Selenoid достаточно удобен в использовании и располагает информативным интерфейсом.
Установка Selenoid
Подготовительные действия для установки Selenoid:
- Необходим установленный и запущенный Docker, так как далее рассматривается использование Selenoid вместе с Docker
- Простейший способ установки Selenoid – загрузить Configuration Manager, который используется для автоматизации установки продуктов Aerokube, коим и является Selenoid
- Переименовать загруженный файл в cm.exe (для удобства взаимодействия)
- Запустить Selenoid командой:
./cm.exe selenoid start --vnc
./cm.exe selenoid-ui start
В результате выполнения команды ./cm.exe selenoid start--vnc произойдёт загрузка образов с VNC-сервером, то есть образов, в которых доступна возможность видеть экран браузера в реальном времени. Также, в процессе выполнения этой команды будет скачана свежая версия Selenoid вместе с контейнерами, исполняемые файлы веб-драйверов, будут созданы файлы конфигурации и последним этапом будет сразу же запущен сам Selenoid.
Следующей командой ./cm.exe selenoid-ui start мы скачиваем и запускаем Selenoid UI – графическую оболочку, через которую мы можем посмотреть ход выполнения наших тестов в реальном времени, видеозаписи выполнения сценариев, примеры конфигурационных файлов, собрать какую-то статистику и т.д.
Selenoid по умолчанию работает на стандартном порту Selenium 4444. Порт можно переопределить, использовав ключ --port.
Selenoid был создан для работы в больших кластерах Selenium и поэтому не имеет встроенного пользовательского интерфейса. Поэтому попытка открыть
Endpoint для тестов
Статистика и сессии Selenoid UI
Selenoid UI доступен по адресу http://localhost:8080/

Здесь мы можем ознакомиться со статистикой и сессиями. Посмотреть текущее использование квоты, ожидающие браузеры и саму очередь. Selenoid UI получает обновления через SSE, поэтому не нужно обновлять страницу в браузере, чтобы увидеть, что происходит. После любых временных сбоев содержимое страницы автоматически обновиться.
Если мы говорим об одновременном тестировании на различных устройствах: например у нас есть кроссплатформенное веб-приложение с функциональностью чата в реальном времени, то мы одновременно можем тестировать взаимодействие между ними, что, безусловно, удобно.
Возможности Selenoid UI
Также в Selenoid UI имеются следующие возможности:

Мы можем выбрать браузер из списка доступных браузеров и пользовательский интерфейс предоставит нам пример настройки с правильными capabilities. На скриншоте видно, что примеры доступны для нескольких языков.

При выборе браузера, мы можем его запустить вручную прямо в интерфейсе. Во время исполнения тестов существует возможность подключится к порту vnc в режиме реального времени и получить доступ к экрану нужного браузера, и даже вмешаться в процесс исполнения автотестов.
Логи и VNC
Если вы используете capability enabaleVnc=true, вы можете увидеть на странице список доступной статистики. VNC позволяет нам видеть браузер и взаимодействовать с ним. В то время, как наш журнал будет отображать все действия браузера.
VNC сессия:

Полноэкранный режим VNC выглядит следующим образом:

Также вы можете видеть журналы контейнера докер для каждого сеанса даже без VNC. То есть, если, к примеру, вы не использовали флаг --vnc при установке самого Selenoid, то вы будете видеть только логи.

Также есть возможность просмотра видео наших тестов. К видео файлам можно попасть, открыв http://localhost:4444/video/, либо перейдя во вкладку “Videos” в Selenoid UI.
Подключение к Selenide проекту Selenoid для запуска своих тестов в Docker контейнере
Для того, чтобы подключить Selenoid, в аннотацию BeforeClass нам необходимо добавить следующую конфигурацию:
Configuration.remote = "http://localhost:4444/wd/hub"; Configuration.browser = "chrome"; Configuration.browserSize = "1920x1080"; DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(capabilityName: "enableVNC", value: true); capabilities.setCapability(capabilityName: "enableVideo", value: true); Configuration.browserCapabilities = capabilities;
Удаляем Property baseurl, которое указывало на каком браузере выполнялся запуск тестов, так как теперь у нас используется свойство
Configuration.browser = «chrome»;
В проекте это выглядит следующим образом:
@BeforeClass public static void setUp() { //Url удалённого веб драйвера Configuration.remote = "http://10.0.75.1:4444/wd/hub"; //Определяем какой браузер будем использовать Configuration.browser = "chrome"; //Размер окна браузера Configuration.browserSize = "1920x1080"; //Создаём объект класса DesiredCapabilities, используется как настройка вашей конфигурации с помощью пары ключ-значение DesiredCapabilities capabilities = new DesiredCapabilities(); //Включить поддержку отображения экрана браузера во время выполнения теста capabilities.setCapability(capabilityName: "enableVNC", value: true); //Включение записи видео в процессе выполнения тестов capabilities.setCapability(capabilityName: "enableVideo", value: true); //Переопределяем Browser capabilities Configuration.browserCapabilities = capabilities;
Дополнительные возможности Selenoid
- Хранение данных в оперативной памяти: в Selenoid все временные памяти хранятся в Tmpfs – временном файловом хранилище, которое позволяет хранить файлы в оперативной памяти. Доступ к ОЗУ, как известно, осуществляется намного быстрее, чем к файловой системе жесткого диска.
- Selenoid позволяет использовать различное разрешение экрана: мы самостоятельно можем настраивать подходящее разрешение экрана для запущенного контейнера. Сделать это можно посредством выставления необходимых параметров в настройках компонента Browser Capabilities.
- Видеозапись тестов: активация записи в Selenoid на примере браузера Google Chrome происходит за счет выставления параметра true в соответствующую настройку компонента Browser Capabilities:
ChromeOptions options = new ChromeOptions();
options.setCapability(“enableVideo”,true);
Использование Selenoid без Docker
Selenoid использует контейнеры для запуска браузеров, однако существуют случаи, когда запуск браузера в контейнере невозможен. Например, в Windows у нас есть Internet Explorer, который нельзя запустить внутри контейнера. Selenoid может использоваться в качестве “легкой” замены сервера Selenium для запуска Internet Explorer, Firefox или Chrome в Windows, например, чтобы использовать Selenoid с Internet Explorer.
Для этого нам потребуется:
1. Загрузить последний архив IEDriverServer и распаковать его в какой-либо каталог (в нашем примере C: \)
2. Загрузить последний бинарный файл Selenoid
3. Создать файл конфигурации browsers.json
Пример фрагмента browsers.json:
{ "internet explorer": { "default": "11", "versions": { "11": { "image": ["C:\\IEDriverServer.exe", "--log-level=DEBUG"] } } } }
4. Запустить Selenoid:
./selenoid_win_amd64.exe -conf ./browsers.json -disable-docker
5. Запустить тесты, используя endpoint http://localhost:4444/wd/hub со следующими capabilities:
browserName = internet explorer
version = 11
6. Для того, чтобы запустить Chrome, необходимо загрузить бинарный файл Chromedriver и, соответственно, изменить browsers.json
7. По умолчанию Selenoid не обрабатывает логи запущенного драйвера, поэтому нужно запустить Selenoid с флагом -capture-driver-logs, чтобы добавить логирование драйверов для каждой сессии в основной лог.
Подводя итог
Решение на основе Selenide + Selenoid в Docker контейнере демонстрирует высокий уровень гибкости в настройке среды исполнения. Стабильность данного решения, значительная экономия времени при его использовании и ряд дополнительных возможностей позволяет оптимизировать процесс и в сжатые сроки обеспечивать высокое качество программных продуктов, в результате легко отдать предпочтение вышеупомянутым решениям, поскольку они позволяют быстро и качественно выполнять задачи по автоматизации тестирования.
