Как проверить скачивание нескольких файлов selenide
Перейти к содержимому

Как проверить скачивание нескольких файлов selenide

  • автор:

Selenide: удобные тесты на Selenium WebDriver

Многие слышали про Selenium WebDriver — один из самых популярных инструментов для написания приёмочных/интеграционных тестов.

Используя Selenium, мы очень быстро заметили, что нам раз от раза приходится писать один и тот же код, чтобы инициализировать браузер вначале, закрыть его в конце, делать скриншоты после каждого упавшего теста и т.д. (пруфлинк).

Поэтому мы решили выделить этот повторяющийся код в отдельную библиотеку. Так на свет появился Selenide.

Что такое Selenide

Selenide — это обёртка вокруг Selenium WebDriver, позволяющая быстро и просто его использовать при написании тестов, сосредоточившись на логике, а не суете с браузером.

Вот пример теста. Как видите, код минимален. Вызвал «open» — и браузер открылся.

@Test public void testLogin() < open("/login"); $(By.name("user.name")).sendKeys("johny"); $("#submitButton").click(); waitUntil(By.id("username"), hasText("Hello, Johny!")); $("#username").shouldHave(cssClass("green-text")); assertThat($("#insuranceDetailsHeader").getText(), equalTo("Страховые полисы")); assertThat($$("#paymentScheduleTable tr").size(), equalTo(7)); >

При вызове метода open Selenide сам запускает браузер и открывает страницу localhost:8080/login (порт и хост конфигурируется, естественно). А также заботится о том, чтобы в конце браузер закрылся.

Дополнительные вкусности Selenide

Selenide предоставляет дополнительные методы для действий, которые невозможно сделать одной командой Selenium WebDriver. Это выбор радио-кнопки, выбор элемента из выпадающего списка, создание снимка экрана, очистка кэша браузера и т.п.

@Test public void canFillComplexForm() < open("/client/registration"); setValue(By.name("user.name"), "johny"); selectRadio("user.gender", "male"); selectOption(By.name("user.preferredLayout"), "plain"); selectOptionByText(By.name("user.securityQuestion"), "What is my first car?"); followLink(By.id("submit")); takeScreenShot("complex-form.png"); >@Before public void clearCache()

И особняком стоит вопрос Ajax: при тестировании приложений, использующих Ajax, приходится изобретать код, который чего-то ждёт (когда кнопка станет зелёной). Selenide предоставляет богатый API для ожидания наступления различных событий:

@Test public void pageUsingAjax()
Я хочу попробовать, с чего начать?

1. Добавь в свой проект зависимость Selenide:

 com.codeborne selenide 1.6  

2. Импортируй парочку классов:

include static com.codeborne.selenide.Navigation.* include static com.codeborne.selenide.DOM.* 

Готово! Пиши тесты, едрён-батон!

Кто-нибудь это реально использует?
  • Java + ANT + JUnit
  • Java + Gradle + JUnit
  • Scala + ANT + ScalaTest

Так что можете быть уверены, проект не сырой, реально используется и поддерживается.
Есть ещё небольшой эталонный open-source проект, в котором используется Selenide: игра Виселица.

Откуда такое название — Selenide?

Библиотека Selenium взяла своё название от химического элемента (Селен). А селениды — это соединения селена с другими элементами.

  • Selenide = Selenium + JUnit
  • Selenide = Selenium + TestNG
  • Selenide = Selenium + ScalaTest
  • Selenide = Selenium + что угодно

Химичьте на здоровье!

UPD. В марте 2013 выпущена версия Selenide 2.0, в которой сильно обновился API. Теперь использование Selenide ещё проще и удобнее.

И ещё мы запустили сайт на русском языке: ru.selenide.org

  • tdd
  • acceptance testing
  • integration testing
  • приёмочные тесты
  • интеграционное тестирование
  • селениум
  • selenium
  • selenide
  • codeborne
  • Веб-разработка
  • Тестирование IT-систем
  • TDD

Selenide Supports Ukraine ����

Selenide API consists of few classes. We suggest you to stop reading, open your IDE and start typing.

Just type: $(selector). — and IDE will suggest you all available options.

Selenide API: Just start typing

Use the power of today’s development environments instead of bothering with documentation!

Selenide API

Just for reference, these are Selenide classes you will probably need for work:

com.codeborne.selenide.Selenide [src] [javadoc]

The core of the library. Main methods are open , $ and $$ (import static com.codeborne.selenide.Selenide.* for readability):

  • open(String URL) opens the browser (if yet not opened) and loads the URL
  • $(String cssSelector) – returns object of the SelenideElement class that represents first element found by CSS selector on the page.
  • $(By) – returns «first SelenideElement» by the locator of the By class.
  • $$(String cssSelector) – returns object of type ElementsCollection that represents collection of all elements found by a CSS selector.
  • $$(By) – returns «collection of elements» by the locator of By type.

Usually, when you get a SelenideElement object by the Dollar $ command, you can perform some action on it:

  • $(byText(«Sign in»)).click();

or even several actions at once:

  • $(byName(«password»)).setValue(«qwerty»).pressEnter();

or you can check some condition:

  • $(«.welcome-message»).shouldHave(text(«Welcome, user!»)).

The “Double Dollar” command ( $$ ) can be useful when a needed element is a one of a same type. For example, instead of:

you can use more readable and verbose alternative:

The majority of operations on elements, acquired by the $ and $$ commands, have built-in implicit waits depending on a context. This allows in most cases to be not distracted by handling explicitly the waiting for loading of elements while automating testing of dynamic web applications.

Don’t be shy to search for more methods inside the Selenide class that can suit your needs. Just type in your IDE Selenide. and choose the needed option among available IDE proposals.

Here are just a few examples:

Look for more details in Selenide gitbook.

com.codeborne.selenide.SelenideElement [src] [javadoc]

The SelenideElement class describes an element found on the page. The object of this class can be acquired e.g. by the $ command. The following useful methods are defined in the class.

Inner elements search methods
  • find(String cssSelector) / $(String cssSelector)
  • find(By) / $(By)
  • findAll(String cssSelector) / $$(String cssSelector)
  • findAll(By) / $$(By)

Here $ and $$ are just more concise “aliases” of find and findAll methods correspondingly.

Thus, you can specify the search path step by step, building the “locators chain”:

$("#header").find("#menu").findAll(".item") 
Methods to check element state — assertions
  • should(Condition) / shouldBe(Condition) / shouldHave(Condition)
  • shouldNot(Condition) / shouldNotBe(Condition) / shouldNotHave(Condition)

We recommend to choose the convenient alias so the line of code can be easily read like a common english phrase, for example:

$("input").should(exist); $("input").shouldBe(visible); $("input").shouldHave(exactText("Some text")); 

Assertions play role of explicit waits in Selenide. They wait for condition ( visible , enabled , text(«some text») ) to be satisfied until timeout reached (the value of Configuration.timeout that is set to 4000 ms by default). You can use “should-methods” explicitly in order to wait the needed state of element before corresponding action, for example: $(«#submit»).shouldBe(enabled).click();

You can also set the timeout explicitly:

$("input").shouldBe(visible, Duration.ofSeconds(30)); 
Methods-actions on element
  • click()
  • doubleClick()
  • contextClick()
  • hover()
  • setValue(String) / val(String)
  • pressEnter()
  • pressEscape()
  • pressTab()
  • selectRadio(String value)
  • selectOption(String)
  • append(String)
  • dragAndDropTo(String)

The majority of actions returns the object of SelenideElement (the same proxy-element) allowing to build concise method chains: $(«#edit»).setValue(«text»).pressEnter(); .

Methods to get element statuses and attribute values
  • getValue() / val()
  • data()
  • attr(String)
  • text() // returns “visible text on a page”
  • innerText() // returns “text of element in DOM”
  • getSelectedOption()
  • getSelectedOptionText()
  • getSelectedOptionValue()
  • isDisplayed() //returns false, if element is hidden (invisible) or if element does not exist in DOM; otherwise — true
  • exists() //returns true, if element exists in DOM, otherwise — false
Other useful methods
  • uploadFromClasspath(String fileName)
  • download()
  • toWebElement()
  • uploadFile(File…)

Look for more details in Selenide gitbook

com.codeborne.selenide.Condition [src] [javadoc]

Conditions are used in should / shouldNot constructs. We recommend to import corresponding conditions statically to receive all the advantages of readable code:

  • visible / appear // e.g. $(“input”).shouldBe(visible)
  • present / exist // conditions to wait for element existence in DOM (it can be still hidden)
  • hidden / disappear // not(visible)
  • readonly // e.g. $(“input”).shouldBe(readonly)
  • name // e.g. $(“input”).shouldHave(name(“fname”))
  • value // e.g. $(“input”).shouldHave(value(“John”))
  • type // e.g. $(“#input”).shouldHave(type(“checkbox”))
  • id // e.g. $(“#input”).shouldHave(id(“myForm”))
  • empty // e.g. $(“h2”).shouldBe(empty)
  • attribute(name) // e.g. $(“#input”).shouldHave(attribute(“required”))
  • attribute(name, value) // e.g. $(“#list li”).shouldHave(attribute(“class”, “active checked”))
  • cssClass(String) // e.g. $(“#list li”).shouldHave(cssClass(“checked”))
  • focused
  • enabled
  • disabled
  • selected
  • matchText(String regex)
  • text(String substring)
  • exactText(String wholeText)
  • textCaseSensitive(String substring)
  • exactTextCaseSensitive(String wholeText)

Look for more details in Selenide gitbook

com.codeborne.selenide.Selectors [src] [javadoc]

The class contains some By selectors to locate elements by text or attributes (that may be missed in standard Selenium WebDriver API):

  • byText — search element by exact text
  • withText — search element by contained text (substring)
  • by(attributeName, attributeValue) — search by attribute’s name and value
  • byTitle — search by attribute “title”
  • byValue — search by attribute “value”
  • Xpath
  • etc.
// Examples: $(byText("Login")).shouldBe(visible)); $(By.xpath("//div[text()='Login']")).shouldBe(visible); // any org.openqa.selenium.By.* selector can be used $(byXpath("//div[text()='Login']")).shouldBe(visible); // or any its alternative from Selectors class 

Look for more details in Selenide gitbook

com.codeborne.selenide.ElementsCollection [src] [javadoc]

This is the class that describes a collection of elements on the page, found by the locator. Usually the object of the ElementsCollection class can be acquired by the $$ method. The class contains rather useful methods.

Assertions
  • shouldBe — e.g. $$(«.errors»).shouldBe(empty)
  • shouldHave — e.g. $$(«#mytable tbody tr»).shouldHave(size(2))

Assertions also play role of explicit waits. They wait for condition (e.g. size(2) , empty , texts(«a», «b», «c»)) to be satisfied until timeout reached (the value of Configuration.collectionsTimeout that is set to 6000 ms by default).

Methods to get statuses and attributes of elements collection
  • size()
  • isEmpty()
  • getTexts() // returns the array of visible elements collection texts, e.g. for elements:
  • a
  • c will return the array [«a», «», «c»]
Methods-selectors of specific collection elements
  • filterBy(Condition) – returns collection (as ElementsCollection) with only those original collection elements that satisfies the condition, e.g. $$(«#multirowTable tr»).filterBy(text(«Norris»))
  • excludeWith(Condition) – e.g. $$(«#multirowTable tr»).excludeWith(text(«Chuck»))
  • get(int) — returns nth element (as SelenideElement );
  • findBy(Condition) — returns the first collection element (as SelenideElement ) that satisfied the condition.

Look for more details in Selenide gitbook

com.codeborne.selenide.CollectionCondition [src] [javadoc]

Collection conditions are used in the shouldBe / shouldHave constructs for the object of ElementsCollection class. It is recommended to import needed conditions statically in order to achieve all advantages of the readable code.

  • empty // e.g. $$(«#list li»).shouldBe(empty)
  • size(int) // e.g. $$(«#list li»).shouldHave(size(10))
  • sizeGreaterThan(int)
  • sizeGreaterThanOrEqual(int)
  • sizeLessThan(int)
  • sizeLessThanOrEqual(int)
  • sizeNotEqual(int)
  • texts(String. substrings)
  • exactTexts(String. wholeTexts)

Look for more details in Selenide gitbook

com.codeborne.selenide.WebDriverRunner [src] [javadoc]

This class defines some browser management methods:

  • isChrome()
  • isFirefox()
  • isHeadless()
  • url() — returns current URL
  • source() — returns source HTML code of current page
  • getWebDriver() — returns the WebDriver instance (created by Selenide automatically or set up by the user), thus giving to the user the raw API to Selenium if needed
  • setWebDriver(WebDriver) — tells Selenide to use driver created by the user. From this moment the user himself is responsible for closing the driver (e.g. by calling getWebDriver().quit() ).

Look for more details in Selenide gitbook

com.codeborne.selenide.Configuration [src] [javadoc]

This class contains different configuration options to configure execution of Selenide-based tests, e.g.:

  • timeout — waiting timeout in milliseconds, that is used in explicit ( should / shouldNot ) and implicit waiting for SelenideElement ; set to 4000 ms by default; can be changed for specific tests, e.g. Configuration.timeout = 6000;
  • browser (e.g. «chrome» , «ie» , «firefox» )
  • baseUrl
  • reportsFolder

Additionally, it is possible to pass configuration options as system properties e.g. when configuration tests execution on CI servers (continuous integration), e.g. -Dselenide.baseUrl=http://staging-server.com/start

Look for more details in Selenide gitbook

Золотой Selenide. Мои лучшие практики по тестированию

Внимание, автор не разделяет понятий чистый автоматизатор или мануальщик, он является инженером, инженером по тестированию который отвечает за то и другое, являясь истинным QA. Но упор в статье пойдет по практикам автоматизации

Тестирование для меня как целая наука, имеющая множество направлений.

Немного важных терминов

Тест — это проверка сценария, expected === actual result.

  1. Регресс тесты — тестирование с возвратом (все работает как работало) или финальная проверка абсолютно всего функционала
  2. Тестирование === соответствие ТЗ и требованиям
  3. Баг — отклонение фактического (actual result) от ожидаемого результата ( expected result )
  4. ASAP — as soon as possible
  5. Без внятного ТЗ — результат ХЗ
  6. RTFT — please, read the f***ing tests

Какие тесты бывают?

  • Unit тесты (их пишут разработчики)
  • UI тесты (их пишу я, и мы остановимся на этом)

Кратко про пирамиду тестирования. Больше всего должно быть юнит тестов, они прогоняются гораздо быстрее UI тестов, помогая разработчикам быстрее обнаружить баги в коде. Работайте по принципу один тест тестирует что-то одно. Между ними кроятся API (or service) тесты.

Мне-тебе не по душе. Какой ЯП выбрать для авто тестов?

Есть три популярнейших ЯП упрощающих жизнь автоматизаторам своими фреймворками и широким комьюнити,

  • Python (Selenium, Unittest/Pytest),
  • Java (Selenide, TestNG/JUnit),
  • C# (тут я не знаю что нужно для .NET-щиков)

Да, еще можно писать на чистом js. Хотя считаю это прошлым веком, но понимать его особо необходимо. На конференции QA от Kolesa Group узнала, что там используют PHP 😀

Итог: выбирайте язык на котором пишут разработчики. Будет у кого спросить в тупиковой ситуации. (Возможно, язык на котором вы сейчас пишете, содержит скелеты в шкафу)

Как я разделяю типы UI тестов

  • Пользовательские сценарии (шаги в системе, приводящие к определенному результату)
  • Сочетания настроек (уместно применить — техники тест дизайна)

Тестировщик, программист или аналитик, вопрос, кто живет в доме? — я скажу тестировщик — любому автоматизатору придется там жить, и далее наша речь пойдет о локаторах.

DOM — document object model. Древовидная иерархия тегов, расположения веб элементов на странице

Что такое веб элемент?

Это все блоки, точки, кнопки, div-ки из которых состоит веб страница. Все что имеет визуально-функциональный блок. Все это WEB ELEMENT-ы. Других названий не поощряю.

Распределение локаторов. Локатор — адрес/путь веб элемента на странице

  • Храните в отдельном файле, так и назовите, Локаторы
  • Для каждой страницы отдельный файл с локаторами — забудьте про это. Вам запарится разделять локаторы по страницам, веб элементы состоят из идентичных форм, css классов, и один локатор часто будет повторяться на разных страницах приложения. Думаете что файл взорвется? Нет, благо в современном мире есть мощные IDE, упрощающих поиск. Около 500 локаторов достаточно покрывают многие потребности по проекту где я работаю (CRM система), в основном использую кастомные генераторы локаторов, об этом позднее.

Используйте интуитивно понятные локаторы, но лучше всего идентификаторы. Прощупайте разницу если написать:

.top-header # Какой то класс div.top-header # Блок, но таких блоков может быть много input.top-header # Ага, поле ввода div.top-header.logo # Ага, сразу понятно, логотип на вверхем хэдере

Длинный локатор не проблема, когда он вынесен в отдельный файл с локаторами:

// ElementLocators.java public class ElementLocators < // Это локатор, считайте как константа, хранящая в себе путь к веб-элемнету public static final String HEADER = "div.top-header-block"; // Это не локатор, а цвет, но можно объявить в этом же файле как константу public static final String RED_COLOR = "rgba(248, 248, 248, 1)"; . >

Дальше я кратко напишу что еще умеет Selenide.

Complex сonditions. Selenide очень удобен написанием однострочников, которые на голом Selenium загромождают тестовый код (хотя такая проверка чрезвычайно избыточна).

$(HEADER).shouldBe(exist, enabled, visible) .shouldHave(attribute("title", "myTitle"), text("TEXT")); /* Состояния: enabled - активный, в основном применяю для проверки кликабельных элементов visible - элемент виден на странице exist - элемент сущетсвует в DOM-е */

Разница между should, shouldBe, shouldHave — абсолютно никакой. Используйте для визуально приятного восприятия (читабельности)

Поиск из множества элементов и их фильтрация.

  • findBy() вернет один веб элемент, а точнее SelenideElement . Все что после $(SOME_LOCATOR) вернет веб элемент селенида.
  • filterBy() отфильтрует и вернет коллекцию. (Коллекция для простого понимания это тот же самый массив)
// Найдет один SelenideElement $$(SOME_DIV).findBy(matchText("Это заголовок")) .shouldBe(enabled).click(); // Работа с коллекциями и их фильтрация. // Коллекция элементов, не входящих в заданный класс: ElementsCollection notCheckedCheckboxes = $$("div.checkboxes").filterBy(not(cssClass("checked")));

Палитра на любой вкус. Проверка веб элемента по его цвету. Селенид краток, лаконичен и удобен, проверка наличия атрибута или сss класса? — не беда

$$(DANGER_BTN).shouldHave(size(1)).first() .shouldBe(visible) .shouldHave(cssValue("background-color", RED_COLOR), cssClass("danger-btn"));

Псевдоклассы. Проверка что чекбокс включен:

 $("input:checked").should(exist);

Работа с localStorage:

LocalStorage localStorage = Selenide.localStorage(); String tokensValue = localStorage.getItem("token"); // получить значение токена localStorage.removeItem("token"); // удалить токен

Расширяем Selenide. Генераторы локаторов. Если у вас на проекте много элементов со схожими родительскими локаторами, полезно и очень удобно использовать генераторы для форматирования локаторов, вот некоторые примеры:

public static String insertIntoTitleStartsWith(String textInTitle) < // ^ нужно для поиска элементов, у которых title начинается с переданного текста return String.format("*[title^='%s']", textInTitle); >public static String insertIntoTitle(String textInTitle) < return String.format("*[title='%s']", textInTitle); >public static String insertIntoAttribute(String attribute, String value)

Немного про XPath

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

Кратко про XPath

И какие есть этому альтернативы? Selenide умеет работать с коллекциями, и брать оттуда первый, последний и N-ный элемент из DOM-a.

/** Если вы хотите быть уверены что все 10 элементов прогрузились, используйте проверки на кол-во. size(), sizeGreaterThan(), sizeGreaterThanOrEqual() */ ElemtntsCollection blocks = $$(BLOCK).shouldHave(size(10)); blocks.first(); blocks.last(); blocks.get(5); // index inside // Или можно использовать $(BLOCK, 5); // Чтобы получить его значение, сокращенное от value() $(BLOCK, 5).val(); // Xpath on Selenide looks like this $(By.xpath("//div")) $(xpath("//div")) $x("//div")

Явные (explicit waits) VS Неявные (implicit waits) ожидания

  • Явные ожидания:
    1. Ждем хардкодно, программа (поток) спит и ничего не делает. Это тот самый sleep
    2. Ожидает наступления условия, каждый раз пингуя определенный элемент. Сразу пойдет дальше при положительном условии. Время ожидания настраивается на один раз и на одно условие.
// Без ожидания условия, спящий поток sleep(1_000); // С ожидание условия. Используются поллинги в течение заданного таймаута $(something).shouldBe(visible, Duration.ofSeconds(10));
  • Неявные ожидания — настраивается как глобальный конфиг таймаута для всех веб элементов. Также ожидает наступления условия и идет дальше. Так мы спасаемся от постоянных настроек Duration.ofSeconds(10) Каждый should содержит в себе неявное ожидание в 4 секунды, которое можно переопределить явным ожиданием в 10 секунд. Не бойтесь, под капотом используется обычный while loop.
/* Эта функция выполнится до запуска теста, и настроит неявное ожидание */ @BeforeMethod public void configureTests() < Configuration.timeout = 5_000; // неявное ожидание в 5 секунд // . Other browser configs >@Test public void simpleTest() < open(URL); sleep(3_000); // явно усыпить поток. Выполнение теста застынет $(ELEMENT).should(exist, Duration.ofSeconds(10)); // явное ожидание в 10 сек >

Паттерны? не, не слыхал

Но возникает еще одна проблема, нагромождение текста, кода, обернутых шагов (лишних действий специфичных для системы), локаторов в самих тест кейсах. Решение: POM, POM, POM. Page Object Model — standard test automation pattern. Паттерн это набор практик, проверенных временем, и прошедших множество костылей. Нельзя просто так взять и понять POM. Напишите свою первую, вторую, третью сотку тестов (я внедрила после четырехсотки), и вы поймете что в них что-то не так, придя к моменту изучите POM.

P.S. сначала нужно наступить на грабли, перед тем как поДнять их

Из чего состоит проверка сценария? Каждый тестируемый случай в моих автотестах состоит из следующего сценария:

/* TEST ID (идентификатор теста) Name (название) Description (описание) Steps to reproduce (шаги воспроизведения) Expected result (ожидаемый результат) Actual result (фактический результат) */

Подробнее разберем состав тест кейса снизу вверх:

  • Ожидаемый результат — версия правильной работы, как и что должно быть
  • Фактический результат — что случилось, на каком моменте сломалось, что произошло
  • Шаги воспроизведения — самая нужная часть тест кейса, приводящая к конечному тестируемому случаю. Мастерство тестировщика в ясности и четкости (краткости) этих шагов. Не пренебрегайте этим пунктом. Без этого тест кейс не тест кейс.
  • Описание — слово само за себя, сюда можно уместить сжатую композицию шагов воспроизведения
  • Название — название функции автотеста, упрощает поиск. Рекомендую использовать или заканчивать название глаголом. SomethingShouldExist. SomethingOfTestingCase.
  • TEST ID — использую для подсчета кол-ва написанных тестов

Здесь дополнительно можно включить компоненту (в какому модулю / блоку / фиче относится тест) и приоритет (серьезность тестируемого сценария).

Лайфхак. Когда падает тест, я просто копирую тест-кейс и вставляю в тикет. Хорошо когда у разработчиков развернут репо с тестами, где они сами могут запустить тест. Рядом пахнет TDD — (selenide) test driven development. В этом случае вы пишете тест до момента UI ошибки, и передаете разработчикам. Думаю это удобно. После фикса дописываете автотест и закрываете кейс.

TDD — это когда тесты написаны до реализации чего либо (функции, модуля, класса, объекта). Тем самым, помогает собрать мысли и сконцентрироваться на задачи, понимая, что должен делать и какой результат должен давать код.

Борьба с flaky тестами

Must have решения для минимизации flaky тестов. Данные решения помогут минимизировать их количество, но никак не избавиться от них полностью

  1. Перезапустить тест. Самый оптимальный вариант при крайних сроках и жестких дедлайнах.
  2. Стереть и переписать тест. Вероятность написать шаги воспроизведения теста немного иным способом высока. Следовательно тест может стать стабильнее. Также подумайте о лишних шагах.
  3. Test retrier — повторитель тестов. Must have штука. Обязательно научитесь. RetryAnalyzer-у (ссылочка). Очень полезная штука, проигрывается N раз, пока тест не загорит зеленым или останется красным. Минусы: действительно упавший тест будет перезагружаться N раз (суммарно займет N * times времени)
  4. UI Trigger — некий UI WebElement, настоящий delay триггер (триггер задержки) на странице, явно дающий понять что событие произошло. Наверняка большинство тестов имеют особенность падать при клике на определенный элемент. Проблема в том что не всегда ясно, завершило ли событие свое действие или имеет не тот атрибут. Например, загрузка файлов, с помощью всемогущего Селенида скачали файл, но как понять что файл был полностью передан с сервера? Поставить sleep на 10 секунд — не вариант. Нужно явно настоять программистам установить спинер или индикатор загрузки на web странице.
  5. JS Injection — об этом напишу словечко. На практике не использовала. Можно внедрить веб элементу css класс или изменить значение атрибута. Для особо отчаянных случаев.

Flaky тесты — нестабильные тесты. Временами падают, давая ложно-положительный результат. Влияют много факторов, javaScript (не до конца отрисован, всплывающие окна, возникновение скроллинга, перекрытие другим слоем или веб элементом), фреймворк, скорость работы железа, сбой в системе, маленький/fullscreen экран, сеть.

Что-то еще полезного

Отключить выполнение скриптов. Иногда очень полезно отключить выполнение js кода в скрипте (слишком быстрая анимация или исчезновение элемента). Сделать это можно прямо с devtools.

F12 Ctrl+Shift+P (Cmd+Shift+P) disable JavaScript

Базовый помощник. Hot keys for IDE. Горячие клавиши — незаменимый помощник в любых ситуациях. Не усложняйте себе жизнь, научитесь им.

// Переход между вкладками ALT + (->, <-) // Чтобы открыть любой файл CTRL + SHIFT + N // Find in files CTRL + SHIFT + F // Format current page CTRL + ALT + O CTRL + ALT + L

Чем же так хорош Selenide?

Вам не нужно думать о закрытии браузера или нагромождать ваши тесты вызовом драйвера, кастомными ассертами как на голом Selenium, Selenide сделает это из под капота. Selenium это низкоуровневый инструмент для непосредственного взаимодействия с браузером, Selenide это фреймворк, разработанный специально для UI тестирования. Разница лишь в ваших целях по применению. Лучшее сравнение Selenide и Selenium написано в официальной wiki

Напоследок

Надеюсь вы смогли почерпнуть что-то полезное для применения. Пробуйте, экспериментируйте, взаимодействуйте с командой, ведь это важнее всего. Буду рада любым предложениям и замечаниям по данной статье.

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

Эффективные UI-тесты на Selenide

Канун Нового Года — время чудес. В преддверии нового года мы все вспоминаем год уходящий и строим планы на следующий. И надеемся, что все проблемы останутся в прошлом, а в новом году случится чудо, и мы заживём по-новому.

Какой же Java разработчик не мечтает о чуде, которое осенит его и позволит стать Самым Крутым На Свете Java Программистом.

Хорошие новости: я хочу рассказать как раз о таком чуде.

Имя ему — автоматические тесты!

Фу, тесты?

Да. Настоящим мастером своего дела вас сделают не чудо-фреймворки, не микро/пико/нано сервисы, а дисциплина. Дисциплина, которая говорит, что программист может считать дело законченным не тогда, когда код готов, а тогда, когда к нему написаны и запущены автоматические тесты. И если с юнит-тестами всё более-менее ясно, то UI-тесты пока остаются для разработчиков тёмным лесом.

Да ну, это же нудно?

О нет, поверьте мне! Написание грамотных автотестов — это хороший вызов, тут есть над чем пораскинуть мозгами. И это может быть очень весело и интересно. Только надо использовать правильные инструменты.

Правильный инструмент для написания UI-тестов — это:

Selenide

Selenide — это библиотека для написания лаконичных и стабильных UI тестов с открытым исходным кодом.

Selenide — идеальный выбор для разработчиков, потому что у неё очень низкая кривая обучения. Вам не придётся заморачиваться со всеми этими техническими подробностям, на которые обычно тестировщики-автоматизаторы тратят так много времени: нюансы работы с браузерами, типичные проблемы с таймингом и аяксом.

Посмотрим, как выглядит простенький тест на Selenide:

public class GoogleTest < @Test public void user_can_search_everything_in_google() < open("http://google.com/ncr"); $(By.name("q")).val("selenide").pressEnter(); $$("#ires .g").shouldHave(size(10)); $("#ires .g").shouldBe(visible).shouldHave( text("Selenide: concise UI tests in Java"), text("selenide.org")); >> 

(естественно, вместо Google здесь будет ваше веб-приложение)

Что здесь происходит?

  • Вы открываете браузер всего-навсего одной командой open(url)
  • Вы ищете элемент на странице командой $.
    Вы можете найти элемент по имени, ID, CSS селектору, атрибуту, xpath и даже по тексту.
  • Вы совершаете некие действия с элементом: в данном случае вводите текст командой val() и нажимаете ввод с помощью команды pressEnter().
  • Вы проверяете результат: ищете все результаты поиска с помощью $$ (она возвращает коллекцию элементов). Вы проверяете размер и содержимое коллекции.

Этот тест легко читается, не правда ли?
Этот тест легко пишется, не правда ли?

А главное, этот тест легко запускается. Убедитесь сами:

Погружаемся глубже

Конечно, в жизни не всё так просто. Написание автотестов подразумевает кучу проблем, ведь не зря разработчики так их боятся — больше, чем любого наисложнейшего фреймворка или технологии.

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

Давайте рассмотрим типичные проблемы UI-тестов подробнее.

Проблемы с аяксом и таймаутами

В наше время веб-приложения все сплошь динамические. Каждый кусочек приложения может быть нарисован/изменён динамически в любой момент времени. Это создаёт проблемы для автоматических тестов. Тест, который ещё вчера был зелёным, может внезапно стать красным безо всяких изменений в коде. Просто потому, что браузер сегодня встал не с той ноги и запустил вон тот javascript чуточку медленнее, и тест успел кликнуть кнопочку раньше, чем она до конца отрисовалась.

Это прям вечная проблема у всех. Поэтому автоматизаторы пихают везде «слипы».

Тем более удивительно, насколько простым и надёжным способом Selenide решает эту проблему.

Если коротко, в Selenide каждый метод умеет немножко подождать, если надо. Люди называют это «умными ожиданиями».

Когда вы пишете

$("#menu").shouldHave(text("Hello")); 

Selenide проверит, существует ли элемент с И если нет, Selenide чуть-чуть подождёт, проверит ещё. Потом ещё подождёт. И только когда элемент появится, Selenide проверит, что у него нужный текст.

Конечно, нельзя ждать вечно. Поэтому Selenide ждёт не больше 4 секунд. Естественно, этот таймаут можно настраивать.

Не сделает ли это мои тесты медленными?

Нет, не сделает. Selenide ждёт, только если надо. Если элемент изначально присутствует на странице — Selenide не ждёт. Если элемент появился через 300 мс — Selenide ждёт только 300 мс. Это именно то, что вам нужно.

Множество встроенных проверок

А что ещё вы можете проверять на странице, помимо текста? Довольно много всего.

Например, вы можете проверить, что элемент видимый (visible). Если пока нет, Selenide подождёт до 4 секунд.

$(".loading_progress").shouldBe(visible); 

Вы можете даже проверить, что элемент не существует. Если элемент всё же найден, Selenide предположит, что он вот-вот пропадёт и подождёт до 4 секунд.

$(By.name("gender")).should(disappear); 

Вы можете делать несколько проверок в одной строке (т.н. «fluent API» и «method chain»), что сделает ваши тесты ещё более лаконичными:

$("#menu") .shouldHave(text("Hello"), text("John!")) .shouldBe(enabled, selected); 
Коллекции

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

Например, вы можете проверить, что на странице ровно N таких-то элементов:

$$(".error").shouldHave(size(3)); 

Вы можете отфильтровать подмножество элементов:

$$("#employees tbody tr") .filter(visible) .shouldHave(size(4)); 

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

$$("#employees tbody tr").shouldHave( texts( "John Belushi", "Bruce Willis", "John Malkovich" ) ); 
Скачивание/закачивание файлов

С Selenide закачивать файлы предельно просто:

$("#cv").uploadFile(new File("cv.doc")); 

Вы даже можете закачать несколько файлов разом:

$("#cv").uploadFile( new File("cv1.doc"), new File("cv2.doc"), new File("cv3.doc") ); 

И скачивание файлов тоже крайне просто:

File pdf = $(".btn#cv").download(); 
Тестирование «динамичных» веб-приложений

Некоторые веб-фреймворки (такие как GWT) генерируют совершенно нечитаемый HTML, не поддающийся анализу. Там нет постоянных ID, имён или классов.

Это прям вечная проблема у всех. Поэтому автоматизаторы пихают везде длиннющие «xpath» и вынуждены их поддерживать до конца жизни.

Чтобы решить эту проблему, Selenide предлагает искать элементы по тексту.

import static com.codeborne.selenide.Selectors.*; $(byText("Привет, хабр!")) // находит элемент по тексту целиком .shouldBe(visible); $(withText("хаб")) // находит элемент по подстроке .shouldHave(text("Привет, хабр!")); 

Вопреки распространённому мнению, поиск элементов по тексту — не такая уж плохая идея. Между прочим, именно так ищет элементы реальный пользователь. Он не ищет элементы по ID или классу, и уж тем более не по XPATH. Он ищет по тексту. (ну, ещё по цвету, но это труднее поддаётся автоматизации).

Ещё в Selenide есть несколько полезных методов для поиска дочерних или родительских элементов. Это позволяет вам навигировать между элементами без опознавательных знаков.

$("td").parent() $("td").closest("tr") $(".btn").closest(".modal") $("div").find(By.name("q")) 

Например, вы можете найти ячейку в таблице по тексту, затем найти содержащую её строку tr и найти в этой строке кнопку «Save»:

$("table#employees") .find(byText("Joshua")) .closest("tr.employee") .find(byValue("Save")) .click(); 
Page Object

Когда один и тот же элемент или страница используется во многих тестах, имеет смысл вынести логику страницы в отдельный класс. Такой класс называется Page Object, и их тоже очень удобно делать с Selenide.

Приведённый выше пример гугла можно переделать на page object таким образом:

 @Test public void userCanSearch() < GooglePage page = open("http://google.com/ncr", GooglePage.class); SearchResultsPage results = page.searchFor("selenide"); results.getResults().shouldHave(size(10)); results.getResult(0).shouldHave(text("Selenide: concise UI tests in Java")); >

Page Object для страницы поиска гугл:

public class GooglePage < public SearchResultsPage searchFor(String text) < $(By.name("q")).val(text).pressEnter(); return page(SearchResultsPage.class); >> 

И для страницы результатов поиска:

public class SearchResultsPage < public ElementsCollection getResults() < return $$("#ires .g"); >public SelenideElement getResult(int index) < return $("#ires .g", index); >> 

Но не злоупотребляйте пэдж объектами.

Хочу обратить ваше внимание, что UI-тестов должно быть мало. По той простой причине, что это всё-таки браузер,
аякс, javascript, а всё это сравнительно медленно и нестабильно. Напишите один-два UI-теста, которые проверят, что
приложение в целом работает: страничка открывается, текст отрисовывается, кнопки нажимаются, JavaScript не грохается.

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

Типичная ошибка — проверять всё через UI. Этим особенно страдают тестировщики в тех компаниях, где разработчики не пишут
модульных тестов. Бедным тестировщикам просто ничего не остаётся, кроме как городить огромную неповоротливую кучу медленных
и нестабильных UI-тестов и впрягаться в их пожизненную поддержку.

Но ты ж программист! Не заставляй людей мучаться.

… и много других полезняшек

В Selenide есть ещё много функций, таких как:

$("div").scrollTo(); $("div").innerText(); $("div").innerHtml(); $("div").exists(); $("select").isImage(); $("select").getSelectedText(); $("select").getSelectedValue(); $("div").doubleClick(); $("div").contextClick(); $("div").hover(); $("div").dragAndDrop() zoom(2.5); и т.д. 

Хорошая новость в том, что вам не нужно всё это запоминать. Просто наберите $, точку и начните писать примерно, что вы хотите. Например «val» или «enter». И посмотрите, какие варианты предложит ваша IDE.

Используйте мощь IDE! Не засоряйте голову деталями и сконцентрируйтесь на бизнес-логике.

image

Сделаем мир лучше

У верю, что мир станет лучше, когда все разработчики будут писать автоматические тесты для своего кода. Когда разработчики будут спокойно вставать в 17:00 и идти к своим детям, не боясь, что они что-то сломали своими изменениями.

Поверьте мне, я чувствую себя уверенно оттого, что пока я ходил за кофе, мои тесты уже проверили мой код. И я точно знаю, что то, что я сдаю в тестирование — работает.

Давайте сделаем мир лучше с помощью автоматических тестов! Будьте уверены в своём софте, и вам не придётся скрещивать пальцы на удачу перед каждым релизом.

selenide-logo

С Новым Годом!
ru.selenide.org

  • tdd
  • acceptance testing
  • integration testing
  • приёмочные тесты
  • интеграционное тестирование
  • селениум
  • selenium
  • selenide
  • codeborne
  • Веб-разработка
  • Тестирование IT-систем
  • TDD

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *