Golden selenide. My best testing practices
Внимание, автор не разделяет понятий чистый автоматизатор или мануальщик, он является инженером, инженером по тестированию который отвечает за то и другое, являясь истинным QA. Но упор в статье пойдет по практикам автоматизации
Тестирование для меня как целая наука, имеющая множество направлений.
Что такое тест?
Тест - это проверка сценария, expected === actual result.
Какие тесты бывают?
Unit тесты (их пишут разработчики)
UI тесты (их пишу я, и мы остановимся на этом)
Кратко про пирамиду тестирования. Больше всего должно быть юнит тестов, они прогоняются гораздо быстрее UI тестов, помогая разработчикам быстрее обнаружить баги в коде. Работайте по принципу один тест тестирует что-то одно. Между ними кроятся API (or service) тесты.
Мне-тебе не по душе. Какой ЯП выбрать для авто тестов?
Есть три популярнейших ЯП упрощающих жизнь автоматизаторам своими фреймворками и широким комьюнити,
Python (Selenium, Unittest/Pytest),
Java (Selenide, TestNG/JUnit),
C# (тут я не знаю что нужно для .NET-щиков)
Да, еще можно писать на чистом js. Хотя считаю это прошлым веком, но понимать его особо необходимо. На конференции QA от Kolesa Group узнала, что там используют PHP :D
Итог: выбирайте язык на котором пишут разработчики. Будет у кого спросить в тупиковой ситуации. (Возможно, язык на котором вы сейчас пишете, содержит скелеты в шкафу)
Как я разделяю типы 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("Это заголовок div-ки"))
.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) {
return String.format("*[%s='%s']", attribute, value);
}
Немного про 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
Явные (explicit waits) VS Неявные (implicit waits) ожидания
-
Явные ожидания:
Ждем хардкодно, программа (поток) спит и ничего не делает. Это тот самый
sleep
Ожидает наступления условия, каждый раз пингуя определенный элемент. Сразу пойдет дальше при положительном условии. Время ожидания настраивается на один раз и на одно условие.
// Без ожидания условия, спящий поток
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 тестов. Данные решения помогут минимизировать их количество, но никак не избавиться от них полностью
Перезапустить тест. Самый оптимальный вариант при крайних сроках и жестких дедлайнах.
Стереть и переписать тест. Вероятность написать шаги воспроизведения теста немного иным способом высока. Следовательно тест может стать стабильнее. Также подумайте о лишних шагах.
Test retrier - повторитель тестов. Must have штука. Обязательно научитесь. RetryAnalyzer-у (ссылочка). Очень полезная штука, проигрывается N раз, пока тест не загорит зеленым или останется красным. Минусы: действительно упавший тест будет перезагружаться N раз (суммарно займет N * times времени)
UI Trigger - некий UI WebElement, настоящий delay триггер (триггер задержки) на странице, явно дающий понять что событие произошло. Наверняка большинство тестов имеют особенность падать при клике на определенный элемент. Проблема в том что не всегда ясно, завершило ли событие свое действие или имеет не тот атрибут. Например, загрузка файлов, с помощью всемогущего Селенида скачали файл, но как понять что файл был полностью передан с сервера? Поставить sleep на 10 секунд - не вариант. Нужно явно настоять программистам установить спинер или индикатор загрузки на web странице.
JS Injection - об этом напишу словечко. На практике не использовала. Можно внедрить веб элементу css класс или изменить значение атрибута. Для особо отчаянных случаев.
Флоки тесты - нестабильные тесты. Временами падают, давая ложно-положительный результат. Влияют много факторов, 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
Напоследок
Надеюсь вы смогли почерпнуть что-то полезное для применения. Пробуйте, экспериментируйте, взаимодействуйте с командой, ведь это важнее всего. Буду рада любым предложениям и замечаниям по данной статье.
Выражаю благодарность каждому разработчику, кто был со мной на проекте. Знаю, я вас часто пингую, но ваша помощь и участие в тестировании бесценны.
Комментарии (11)
dyadyaSerezha
08.09.2022 23:08+1Есть ещё современная связка Gauge + Taiko. Gauge - мультиязыковой движок тестов (js, Python, etc). Taiko - средство работы с браузерами.
AndreyGuc
09.09.2022 00:35+11) Явные / неявные ожидания напутали.
2) "Из под коробки" странно звучит. Корректно "из коробки" ll "из под капота".
3) В пирамиде потеряли минимум 1 слой. API тестирование например.
4) На Js можно и нужно писать автотесты. Выбор фреймворков великолепный: Playwright, WebdriverIO, Cypress, Puppeteer и прочее. При этом у многих принцип plug and play + поддержка API тестирования.
dark-tulip Автор
09.09.2022 07:44Возможно, я переструктурировала параграф с локаторами, опираясь на эти ссылки, selenium и stepik, настройка ожиданий. Из под капота действительно звучит точнее, поправила) Да, в промежутке есть api (service) тестирование, к сожалению пока им не занималась. Про js затрудняюсь ответить.
sashaxorev
09.09.2022 12:20+1Мне кажется что js/ts экосистема для ui тестов намного больше и обширнее, чем c#.
Про вынесение selectors в отдельный файл и отрывание их от контекста использования, имхо довольно спорное решение.
Почитайте про EOM (element object model ), узнаёте много нового
dark-tulip Автор
09.09.2022 12:29Хмм, интересно, спасибо за сводку)
выделение локаторов помогло избежать их исправления во всех тестах где он используется, думаю тут нужно ориентироваться по проекту))
AlexeyALV
Видно, что написано из практики, хоть и немного сумбурно.
Про локаторы- в идеале бы договориться с разрабами о системе уникальных id для элементов.
Отчеты на чем делаете? Allure пробовали?
dark-tulip Автор
Про id, используем кастомный атрибут [selenide-id="something"] так как #id автогенерируемый и используется для других целей. Отчеты минимальны, gradle/testng. Про allure, да локально поднимаю, визуально приятный инструмент, глядя на него подметила что забыла написать про аннотации и приоритеты.
AlexeyALV
А запуск тестов руками или через Jenkins или подобное?