Данная статья будет полезна начинающим 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 контейнере демонстрирует высокий уровень гибкости в настройке среды исполнения. Стабильность данного решения, значительная экономия времени при его использовании и ряд дополнительных возможностей позволяет оптимизировать процесс и в сжатые сроки обеспечивать высокое качество программных продуктов, в результате легко отдать предпочтение вышеупомянутым решениям, поскольку они позволяют быстро и качественно выполнять задачи по автоматизации тестирования.