Selenide — практикум

Описание

Selenide — бесплатный фреймворк с открытым кодом, обертка вокруг WebDriver, дающая доступ ко всем возможностям Selenium, при этом работает более упрощенно, что позволяет сосредоточиться на главных вещах происходящих в браузере. Проще управление браузерами, плюс фичи типа автоматических скриншотов при падении теста. Потом, оптимизированный API для работы с браузером, включая функции, недоступные из «простого» Selenium, и более удобные стандартные функции.

Maven

Начнем знакомство с настройки Maven, добавив Selenide в проект. Там одна зависимость, добавляемая в билд:

<dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>selenide</artifactId>
    <version>6.15.0</version>
    <scope>test</scope>
</dependency>

Последняя версия — в главном репозитории Maven. Автоматически подтянется все нужное, включая последнюю версию Selenium WebDriver.

Также понадобится тестовый фреймворк — в нашем случае это будет JUnit. Он и будет использоваться для написания тестов.

Первый тест

Теперь, когда зависимости поставлены, пишем первый тест. Мы проверим, что например этот сайт будет первой строкой выдачи в поисковой системе как и должно быть. Первое, что нам понадобится — импорты. Они нам не так уж нужны, но с ними гораздо легче читать код:

import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;
import org.openqa.selenium.By;

И теперь пишем тест:

@Test
public void searchBaeldung() throws Exception {
    open("https://duckduckgo.com/");

    SelenideElement searchbox = $(By.id("searchbox_input"));
    searchbox.click();
    searchbox.sendKeys("Baeldung");
    searchbox.pressEnter()

    SelenideElement firstResult = $(By.id("r1-0"));
    firstResult.shouldHave(text("Baeldung"));
}

Первое, что бросается в глаза — как мало кода. Здесь нет ничего, связанного с управлением браузером, ожиданием загрузки и прочими вещами, которые усложняют тесты. Вместо этого все, что мы здесь видим, имеет непосредственное отношение к браузерному тесту.

Начинаем мы с того, что открываем указанную веб-страницу. Затем щелкаем в строке поиска, вводим наш поисковый запрос и нажимаем Enter. Далее находим первый результат (ведь мы знаем, что r1-0 — это идентификатор, присваиваемый первому результату) и убеждаемся, что он содержит ожидаемый текст.

Нотация $(), которую мы видим здесь, является инструментом запроса к странице браузера. Она принимает селектор и возвращает SelenideElement, представляющий первый подходящий элемент на этой странице. У нас также есть $$(), которая возвращает коллекцию всех подходящих элементов.

Получив SelenideElement, мы можем продолжить уточнение, с помощью того же паттерна, только теперь это будет SelenideElement.$ и SelenideElement.$$.

Получив SelenideElement, мы можем начать работать с ним — например использовать такие методы, как click(), sendKeys() и pressEnter().

Мы также можем сделать предположение-assertion, что элемент соответствует ожиданиям, используя методы should(), shouldBe() и shouldHave(). Эти три метода идентичны, и сформулированы по-разному только для того, чтобы тест лучше читался — например, firstResult.should(text("Baeldung")); был бы функционально идентичен, но хуже читался бы.

Page Object

Page Object — полезный паттерн [Здесь полный гайд по РОМ], который мы можем (и будем) часто использовать при тестировании UI. Он предполагает написание классов, инкапсулирующих целые веб-страницы, или их части. Затем мы можем использовать эти объекты в тестах. Полезность паттерна в том, что если мы меняем что-то на странице, то мы меняем лишь объект, и каждый тест, использующий его, будет корректным.

Selenide позволяет использовать POM-паттерн так же легко, как и Selenium WebDriver. Мы можем писать классы, которые напрямую используют классы Selenide. Либо с помощью статического метода $, который позволяет работать со всей страницей, либо обернув значение SelenideElement, представляющее отдельную часть страницы.

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

public class SearchFormPage {
    public void open() {
        Selenide.open("http://duckduckgo.com/");
    }

    public void search(String term) {
        SelenideElement searchbox = $(By.id("searchbox_input"));
        searchbox.click();
        searchbox.sendKeys(term);
        searchbox.pressEnter();
    }
}

Здесь два метода, которые мы можем использовать: один для открытия страницы поиска, а другой — для выполнения поиска по заданному термину.

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

public class SearchResultsPage {
    public SearchResult getResult(int index) {
        SelenideElement result = $(By.id("r1-" + index));
        result.shouldBe(visible);

        return new SearchResult(result);
    }
}

Это дает нам метод получения доступа к одному результату. Он возвращает другой объект страницы, на этот раз обернув в него один элемент SelenideElement:

public class SearchResult {
    private SelenideElement result;

    public SearchResult(SelenideElement result) {
        this.result = result;
    }

    public String getText() {
        return result.getText();
    }
}

Затем этот объект страницы оборачивает этот SelenideElement и позволяет нам взаимодействовать только с ним. В данном случае — получить текст из результата поиска.

Теперь мы можем написать тест так:

@Test
public void searchBaeldung() {
    SearchFormPage searchFormPage = new SearchFormPage();
    searchFormPage.open();
    searchFormPage.search("Baeldung");

    SearchResultsPage results = new SearchResultsPage();

    SearchResult firstResult = results.getResult(0);
    assertTrue(firstResult.getText().contains("Baeldung"));
}

Этот тест такой же, как и предыдущий, но теперь мы пишем его в терминах классов, которые лучше объясняют происходящее. Преимуществом этого является то, что тесты стали более читабельными для людей, не вникающих как рендерится HTML.

Это также дает возможность удобнее изменять взаимодействия со страницами. Например, если изменится способ поиска отдельного результата, то нужно будет изменить метод getResult(), а не менять по отдельности множество различных тестов.

Отработка падающего теста

Важной деталью при написании тестов является возможность легко определить причину их падения. Без этого можно потратить кучу времени на определение причины.

Давайте попробуем изменить наш исходный тест так, чтобы он падал. Для этого настроим его на поиск неправильного термина:

SelenideElement searchbox = $(By.id("searchbox_input"));
searchbox.click();
searchbox.sendKeys("Something Else");
searchbox.pressEnter();

Запуск этого теста, очевидно, приведет к падению — поиск по запросу «Something Else» не найдет Baeldung в качестве первого результата. Как это происходит? Сбой происходит на первом утверждении-assert’е:

А как выглядит фактическая ошибка?

Здесь мы сразу же видим не только HTML-элемент, на который мы подали assertion, но и точное значение из него. В данном случае мы выполняем assertion на текстовом содержимом.

Но это еще не все. Мы сделали скриншот и записали исходник страницы. Открыв их, мы можем увидеть точное состояние веб-страницы на момент возникновения ошибки:

Сразу же, глядя на этот скриншот, мы видим, что искали не то, что нужно, и поэтому получили неправильные результаты поиска. Это поможет нам выявить проблему и легко исправить тест.

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

Конфигурация Selenide

Мы только что видели, как Selenide сохраняет скриншоты упавших тестов. Но ему необходимо знать, куда помещать эти скриншоты.

Selenide поставляется с правильным поведением по умолчанию. Например, наши скриншоты по умолчанию сохраняются в папку build/reports/tests. Однако настройки по умолчанию не всегда подходят, поэтому нам необходимо уметь их настраивать. Это можно сделать одним из трех способов:

  • С помощью файла свойств
  • С помощью системных свойств
  • Программно в коде

Самый приоритетный способ управления этими настройками — в коде. Для этого мы изменяем свойства объекта com.codeborne.selenide.Configuration во время тестирования. Это может быть непосредственно в самом тесте, в методе @BeforeEach или в любой другой точке, которая будет выполняться в нужное время. Все эти свойства имеют JavaDoc, объясняющий, что именно они означают, чтобы мы могли правильно их изменить.

Следующим по приоритету является использование системных свойств. Мы можем задать их стандартным образом, как мы задаем системные свойства тестам. Имена свойств такие же, как имена полей в классе Configuration, только с префиксом "selenide.". Например, selenide.reportsFolder соответствует Configuration.reportsFolder.

Наконец, мы можем определить все эти свойства в файле selenide.properties в корневом classpath. Например, в maven-проекте он будет в каталоге src/test/resources. В этом файле свойства называются так же, как и системные, но они существуют внутри проекта, тогда как системные свойства могут быть заданы извне — например, нашей CI-системой.

Наиболее важными для понимания здесь являются свойства, связанные с использованием браузера и местом хранения файлов. Например, мы можем использовать Firefox вместо Chrome:

  • Программно установив в коде Configuration.browser = "firefox";.
  • Добавив в командную строку команду -Dselenide.browser=firefox.
  • Добавив selenide.browser=firefox в файл selenide.properties.

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

/

Код всех примеров на GitHub

Источник


Божественное тестирование в телеграме

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

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

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

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

Мы в Telegram

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

? Популярное

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

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

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

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

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

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

live

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