Автоматизация без Selenium

se2

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

Я надеюсь для всех моих читателей выступать в этой статье, как Капитан Очевидность, рассказывая о тех вещах, которые известны и понятны всем вам. Но к сожалению мой горький опыт показывает, что очень много людей не понимают очевидных вещей и сами же страдают от этого.

Не буду сильно углубляться в описание, мы знаем, что Selenium – это инструмент взаимодействия с современными браузерами, который может использоваться в целях автоматизации тестирования web-приложений (но не обязательно). С его помощью мы можем эмулировать все те действия, что пользователь может предпринять в браузере, таким образом проверяя все необходимые сценарии использования нашего продукта.

Selenium  — продукт с богатой историей, многократно и повсеместно используемый, переживший несколько обновлений, имеющий большое сообщество пользователей, огромное количество статей, блогов и даже книг. Естественно, что сложилась неверная ситуация когда мы говорим Selenium, подразумеваем автоматизацию тестирования, говорим автоматизация – подразумеваем Selenium (или его клоны в том числе и для других языков программирования).

Казалось бы, в чем проблема? Молодые (и не очень) ребята проходят некие курсы по Selenium или занимаются самостоятельно по книгам, блогам, видео и выкладывают свои резюме с гордыми записями в стиле Автоматизатор, QA Automation или даже Software Developer in Test (в этом месте слышен шлепок facepalm’а разработчиков). Но потом не могут найти работу, не проходят собеседования или, что хуже, приходят на работу, но не могут с ней справиться.

Давайте разбираться, что же тут не так, ребята же 2 недельные курсы прошли, книжку прочитали…

no-ya-zhe_28002397_orig_

  • web-приложение — это не только и не столько графический интерфейс (далее ГУИ). По сути ГУИ — это только фасад с которым взаимодействует ваш пользователь, самое важное как всегда — это информация, ее трансформация, обработка и так далее. Крайне сложно представить приложение, где самое важное – это графика в браузере, а не стоящая за ней информация, которая этой графикой визуализируется. Даже в браузерных играх, которые я имел удовольствие тестировать, графика конечно важна, но гораздо важнее правильность обработки всего того, что происходит за кадром. Проще говоря пользователь еще может смириться с тем, что выстрел его рельсы был не того цвета, но даже не думайте начислить ему на 1 кристалл меньше или при этом выстреле нанести дамаг ниже ожидаемого. Согласитесь, что и пользователи ваших приложений скорее могут смириться с поехавшей версткой или неплавными переходами меню, чем с некорректными данными или с их отсутствием. Можно конечно сверять данные и с помощью Selenium, но для этого есть более простые и надежные способы.
  • Про критичную важность и маст-хев юнит-тестов я даже не буду говорить, это должно быть всем ясно, но в чем их особая сила? Они тестируют максимально малый участок кода, максимально близко к нему и максимально быстро, тем и хороши. В чем проблема Selenium-тестов? Между нашим кодом и приложением есть посредники, а именно API самого Selenium и собственно используемый браузер, то есть к проблемам (возможным) нашего кода и/или приложения добавляются проблемы как программные так и с временем выполнения от Selenium и браузера. В эту цепочку кроме того часто встраивают еще и обертку над Selenium или прокси. О надежности и скорости выполнения таких тестов думаю все в курсе.
  • ГУИ –самая простая и логичная вещь в плане проверки простыми тестировщиками (я не люблю терминов «ручные» тестировщики, так как мы их не приручили, не люблю и «ручники», так как ногами тоже никто не тестирует, в данном случае под «простыми тестировщиками» я имею в виду тех, кто не использует автоматизацию на языках программирования). Среди простых тестировщиков есть те, кто умеют работать с БД или скажем SOAP UI(Postman), но проверять ГУИ, согласитесь можно научить кого угодно, да и это уже все умеют, достаточно дать теорию. То есть многие вещи из наших тестов могут и должны проверять простые тестировщики, в том числе те, кто знаком с исследовательским тестированием. А с помощью кода проще и логичнее проверять те стороны продукта, которые простым тестировщикам недоступны в силу отсутствия знаний и соответствующих инструментов. То есть простые тестировщики тестируют черный ящик, а уж автоматизатор должен лезть внутрь этого ящика и проверять серый или прозрачный ящик.
  • Современный ГУИ уже редко пишется руками и на чистом javascript, все чаще его строят на основе имеющихся фреймворков. А элементы и блоки этих фреймворков уже многократно протестированы как их разработчиками, так и многими пользователями этих фреймворков по всему миру. Дополнительно проверять скроллы, календари и кнопки в них нет смысла, важны только данные, которые придут и как они будут обработаны.

В итоге мы приходим к тому, о чем много раз говорилось:

а) Selenium тесты должны быть на вершине пирамиды тестирования и проверять совсем немного сценариев;

б) больший упор в ГУИ должен быть отдан на проверку простым тестировщикам, исследовательское тестирование rules!

в) должны быть тесты API приложения и/или его частей, которые можно проверить изолированно

г) приложение должно быть максимально покрыто юнит-тестами (да, я знаю, что их не пишут)

в итоге наш автоматизатор должен появляться в пункте «в» по максимуму и совсем немного и в последнюю очередь в пункте «а»

Что же должен уметь и знать автоматизатор по факту, а не после просмотра видео на ютубчике:

securityserv1) Главное правило автоматизации тестирования «Изучай язык программирования, а не автоматизацию на нем!» (с)

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

Для  языка Java автоматизатор должен знать:

— основы (да-да!) примитивы, объекты, как работает сборщик мусора, модификаторы доступа

— ООП, не просто прочитать и выучить определения, а понимать что это, как реализуется в коде, почему ругают наследование, в чем сила полиморфизма, для чего нужна композиция

— коллекции! Без этого просто невозможно, как минимум List, Set, Map и их основные реализации

— многопоточность, особенно понимание блокировок, взаимодействия потоков, общих данных

— работа с http запросами, на чистой Java или используя библиотеки.

— работа с БД с помощью jdbc

— паттерны (шаблоны), нужно знать хоть что-то еще кроме ПейджОбджект и Синглтон, рекомендую Команда, Стратегия, Декоратор, Строитель

— логирование

— нужно знать еще один тестовый фреймворк. Если чаще работаете с TestNG то знать и Junit и т.п.

— умение писать юнит-тесты (для своего кода)

— парсинг данных и все что с этим связано

— умение работать со сборщиками maven/gradle

— Java 8 фичи – это обязательно! Стримы, лямбды, функциональные интерфейсы, методы по умолчанию

— вы будете смеяться, но нужно знать и уметь работать с Selenium: локаторы, логи, ожидания, actions, вот это вот все

Для других языков программирования нужны аналогичные знания

securityserv2)  Не помню у кого прочел (или Болтон или Виноградов) но все мы тестировщики изначально, а все остальное, в том числе автоматизация – это уже не так важно. Болтон вообще считает разделение на автоматизаторов и простых тестировщиков вредной. Это я к тому, что знание всего из пункта 1 не освобождает автоматизатора от знаний теории тестирования, цикла разработки, умения писать баги, проводить исследовательское тестирование.

securityserv3) Есть довольно обширная, но не очень глубокая зона сопутствующих знаний, которые должны быть у автоматизатора:

— понимание как работает браузер, понимание самого протокола http, кодов ответа, rest.

— знание основ SQL

—  умение работать с системой контроля версий

— понимание что такое json, xml

 

Проблема, которую я хотел описать не только в низком уровне «автоматизаторов». Что важно, сами компании  (наши работодатели) ассоциируют автоматизацию с Selenium и не ищут автоматизатора там, где он бы пригодился, потому что не знают что он ДОЛЖЕН решать и такие проблемы, а не только кликать в браузере. То есть с одной стороны мы имеем не самых хороших кандидатов, а с другой стороны формирование мнения (частично справедливого) о невысоком уровне автоматизаторов и их ненужности на серьезных проектах, где важнее данные, а не картинка. И эту ситуацию нужно менять, в первую очередь начиная с себя и своих умений.

Надеюсь, эта статья толкнет некоторых на пересмотр своего резюме, а главное на изучение дополнительных технологий. Но мне будет достаточно и «Спасибо, Кэп!»

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

 Software-Testing.Ru

Реклама

XPATH, вверх и вниз по DOMу

xpath

Итак, мы посмотрели как писать локаторы XPATH, как писать CSS, договорились не копировать локаторы слепо и по возможности писать кратко и понятно.

Напоминаю алгоритм написания любого локатора:
1) Если есть, то используем уникальный id или имя класса
2) Если не выполняется пункт 1, то идем к разработчикам и просим его обеспечить
3) Если не выполняется пункт 2, то пытаемся использовать CSS, если можно написать коротко
4) Keep calm and use XPATH

Сегодня я покажу написание локаторов на небольшом и конечно искусственном примере, который показывает, что не всегда можно использовать CSS или другие локаторы. Перед нами сайт дорого во всех смыслах Ростелеком, с предложениями по интернет: https://spb.rt.ru/packages/tariffs

В тестах нам нужно нажать кнопку «Подключить» у тарифа с ценой 1010 рублей. Но мы при этом знаем, что тарифы часто меняются и данный тариф с его кнопкой может быть расположен в любом месте страницы, с любым id. (На самом деле задача реальная, но любые совпадения случайны)
1
Исходя из условий, мы не можем просто получить список кнопок с надписью «Подключить» и кликнуть вторую, в любой момент она может стать третьей. Мы также не можем быть уверены, что id тарифа не изменится, кроме того, было бы круто иметь один общий локатор, который бы нашел кнопку «Подключить» у тарифа по его цене. Класс также не уникален и возвращает список элементов.

2

Что же есть уникальное, к чему можно прицепиться? Цена тарифа! У всех тарифов разные цены, причем в данном случае мы как раз ищем цену 1010 рублей.3

У элемента цены есть уникальный атрибут data-fee=’1010′ по которому мы можем точно найти его, причем только его одного.

//*[@data-fee=’1010′]  — мы точно находим нужную цену. Теперь нужно двигаться к кнопке Подключить по дереву DOM и тут есть два варианта:

1) подняться от цены к ее родителю (classtariff-desc__cost_m-cell«), от него получить следующий сестринский элемент (classtariff-desc__cost_l-cell«) и уже в нем получить кнопку. Получается один шаг вверх и два вниз

4

//*[@data-fee=’1010′]/.. — переходим к родителю

//*[@data-fee=’1010′]/../following-sibling::div — уходим вниз к сестринскому элементу

//*[@data-fee=’1010′]/../following-sibling::div/a — получаем нашу кнопку

2) так как кнопка Подключить и цена входят в состав одного блока, то гораздо лучше от цены найти ее предка (не непосредственного родителя!), который является этим блоком (classtariff-desc__cost tariff-desc__cost_reset js-price-blocks«) у него просто взять вложенный элемент кнопки. Получается всего два шага -вверх и вниз

5

 

//*[@data-fee=’1010′]/ancestor::div[contains(@class,’js-price’)] — получаем предка(ancestor), с классом содержащим js-price

//*[@data-fee=’1010′]/ancestor::div[contains(@class,’js-price’)]//a — у нашего элемента просто ищем вложенную ссылку (нашу кнопку).

Можете убедиться, что оба локатора являются уникальными, и меняя цену, можно получать локаторы кнопки у всех остальных тарифов по их цене.

Я чаще всего работаю с элементами во вкладке Элементы инструментов разработчика браузера, чтобы видеть соседние элементы, предков и вообще дерево DOM. Но иногда удобнее проверять локаторы во вкладке Консоль, набрав команду вида $x(«выражение») , там видно только конкретно найденные элементы

6

 

Надеюсь вам никогда не придется идти в алгоритме поиска элемента дальше 1 пункта.

 Software-Testing.Ru

Регистрация и забытый пароль с временными ящиками почты

BBrbB4i.img

Решил написать небольшую заметку по поводу простого способа проверить регистрацию и восстановление пароля через почту. Как известно в большинстве сервисов регистрация идет через электронную почту, аккаунт привязывается к ней, восстановление пароля идет через отправку письма на нее же. Я встречал в сети описания как можно разворачивать собственный почтовый сервер и отправлять письма туда и другие, не самые простые способы. Они порой обусловлены недоступностью сети интернет и другими факторами и имеют право на жизнь. Я же предлагаю простой способ, который доступен, если выход в интернет все же есть — сервисы временной почты, с простым API.

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

В итоге ищем бесплатные сервисы, предоставляющие не только почту, но и API для нее. Платные я сразу же не рассматривал и в итоге нашел сходу post-shift.ru, а также Guerilla Mail. В обоих случаях мы имеем очень простое API (только GET запросы, с параметрами в адресной строке), позволяющее создать новый почтовый ящик, проверять список писем, получать текст письма (для того чтобы получить вожделенную ссылку для подтверждения регистрации или восстановления пароля).

Особенности post-shift:

— создает обычный ящик на 10 минут, можно продлить время, если оно уже не истекло

— можно создать ящик с заданным именем на 1 час

— позволяет получать ответы как в виде json так и простым текстом

— можно очистить ящик, но нельзя удалить конкретное письмо

Особенности guerilla mail:

— позволяет создать новый ящик и не удаляет его, то есть сохранив токен, можно обращаться к нему и потом

— все ответы приходят только в формате json

— позволяет удалять письма из ящика, но нет возможности очистить ящик сразу

— позволяет задать имя для ящика, то есть текст который идет до знака @

— в тексте письма удаляет iframe, javascript код, апплеты

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

Лично мне понравились оба сервиса, у них есть различия из которых и можно исходить делая выбор

Для своего удобства накидал небольшие модули для использования API этих сервисов, правда на языке Python – так и быстрее и код короче, чем на Java. Можно посмотреть вот тут

Software-Testing.Ru

Сравниваем скорости поиска элементов

cross

Я уже не раз писал, как и многие до меня, что несмотря на распространенное мнение поиск с помощью XPATH  не медленнее поиска с помощью CSS локаторов. Выдалась свободная минутка и решил потестить немного скорость поиска элемента с помощью разных локаторов и в разных браузерах: Chrome, Firefox, InternetExplorer. Естественно, замеры относительны и воспроизводимы с теми же цифрами только на моей машине, однако есть интересные тенденции.

В Python есть удобная утилита timeit в стандартной библиотеке, к сожалению не смог найти что-то похожее на Java, поэтому просто накидал метод который 100 раз подряд находит элемент по заданному  локатору и выдает среднее затраченное время.

Поиск проводил на странице https://google.ru  и по идее искал я один и тот же элемент, просто задавал различные локаторы для него. Время в миллисекундах.

Браузер

XPATH

CSS

ID

CLASS

NAME

‘//form[@class=’tsf’]’

‘form.tsf’

‘tsf’

‘tsf’

‘f’

Chrome 6-7 6-7 7 6 6-7
Firefox 3-5 4-5 3-5 3-4 3-4
IE 50 (!) 78(!!) 77-80 77-79 76-79

Я конечно знал, что Chrome быстрый, в последнее время я работаю только с ним, но не ожидал, что Internet Explorer будет настолько медленнее, искренне сочувствую тем, кому приходится тестировать УИ на нем!

Второй неожиданностью стало то, что Firefox пусть и немного, но побыстрее Chrome.

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

Естественно, замеры я проводил не один раз и в разном порядке, как 1  локатор за сессию, так и все подряд.

Не успокоившись на этом, запустил консоль Python, который сейчас активно осваиваю, и воспользовался утилитой timeit, которая выдает среднее время за указанное количество повторов функции. Локаторы и сайт естественно остались теми же. Цифры были уже другие, но закономерности сохранились, а именно: Firefox немного быстрее Chrome, Internet Explorer на порядок медленнее обоих браузеров, но при этом поиск по XPATH у него работает гораздо бодрее всех остальных.

Итак, выводы:

1) поиск по XPATH не быстрее CSS, а для браузера Internet Explorer даже наоборот, есть смысл все локаторы писать только на XPATH!

2) Chrome не самый быстрый браузер, пусть и совсем чуть-чуть, но Firefox ищет побыстрее, видимо обновления пошли на пользу Лисе

3) Internet Explorer must die!

4) поиск по id или class имеет только преимущество в удобстве написания локатора, если есть уникальные id или имя класса. Вопреки некоторым встреченным в интернете рассказам, поиск по id или class не быстрее остальных видов поиска

Вот тут пожно почитать про xpath, а вот тут про css!

Software-Testing.Ru

 

 

 

 

Еще немного о WebDriver

item1313068_600px

Решил добавить еще немного информации, о которой не все, как мне кажется, знают и не на всяком курсе рассказывают. Кому-то может показаться любопытным:

securityservСуществует 2 способа паузы, реализуемой через Thread.sleep() непосредственно в библиотеке вебдрайвера. Кроме описанного вот тут, есть еще интерфейс Sleeper, правда все его удобство кончается на возможности задания точного периода ожидания, от необходимости обрабатывать InterruptedException вы не избавлены!

Sleeper.SYSTEM_SLEEPER.sleep(new Duration(3,TimeUnit.SECONDS));

Это чисто для ознакомления, надеюсь вы не будете это использовать в коде тестов для ожидания элементов. Лично мне не ясно для чего такое дублирование кода в библиотеке и почему в Actions при вызове pause() не вызывать Sleeper.

securityservЕсть специальный класс для безопасного использования вебдрайвера в многопоточном тестировании, который рекомендуется для любого многопоточного использования.

webDriver = ThreadGuard.protect(webDriver);

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

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

То есть, это скорее способ дополнительно гарантировать себе, что драйвер используется безопасно, как только видите исключение в стиле

Exception in thread "Thread-0" org.openqa.selenium.WebDriverException: 
Thread safety error; this instance of WebDriver was constructed on thread main (id 1) and is being accessed by thread Thread-0 (id 67)
This is not permitted and *will* cause undefined behaviour

нужно принимать меры.

securityservВ библиотеке WebDriver есть свой класс для работы с файлами и директориями FileHandler. Его возможности вы можете посмотреть в документации, а я приведу лишь один пример. Скажем для создания скриншота в основном используют класс из google-guava или apache-commons, но это можно сделать и так, не используя сторонних библиотек

public void make(String fileName) {
  File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE)
     try {
       new FileHandler().copy(scrFile, new File(fileName));
     } catch (IOException e) {
        //логируем и т.д.
     }
}

securityservКроме всем известных способов задания метода поиска By, есть и методы работающие сразу с набором элементов — ByAll и ByChained.

Применяя класс ByAll, мы собираем все элементы, которые соответствуют указанным локаторам. Внимание, будут найдены все элементы, удовлетворяющие каждому из локаторов, а не им всем. Это может быть удобно, если нам нужен список различных элементов: к примеру(ну так случилось!) у вас на странице много кнопок, но локаторы у них разные. С помощью этого класса, можно получить список всех кнопок, перечислив все локаторы.

driver.findElements(new ByAll(By.id("a"), By.id("b")));

— вернет список элементов, в котором будут все, соответствующие локатору id=’a’, а кроме того все, соответствующие локатору id=’b’. То есть это аналогично результату, если бы мы нашли список элементов id=’a’, нашли список id=’b’ и смержили два списка в один.

Второй класс ByChained действует по-другому – он возвращает все элементы последнего локатора, которые при этом расположены внутри элементов с начальными локаторами, то есть являются их потомками. Может быть полезен, если нужны потомки какого-то класса, скажем элементы выпадающего списка.

driver.findElements(new ByChained(By.id("a"), By.id("b")));

— вернет список элементов с локатором id=’b’ (!), которые при этом потомки (вложены) в элемент(ы) с локатором id=’a’.

Если в первом варианте (ByAll) мы можем спокойно перепутать локаторы местами – все равно вернет все элементы, то во втором случае (ByChained) нужно быть точным в порядке локаторов, иначе вернется пустой список.

securityservЛюбителю ОС Windows будет интересно узнать, что в библиотеке есть класс с многозначительным названием WindowsUtils. Как и большинство (все?) утилитных классов он содержит статичные методы, нет необходимости создавать объект класса. Умеет он не так уж много, из полезного можно определить является ли текущая ОС действительно виндой

WindowsUtils.thisIsWindows();  // возвращает true рабам Микрософт :-)

И если работаем именно в Окнах, то можно, к примеру, убить процесс по его имени или id. Думаю все в курсе, что в случае падения теста по исключению (не ассерту) или если забыли вызвать driver.quit()  — процесс драйвера продолжает висеть в работающих, даже если закрыть браузер. С помощью утилит, его можно убить, скажем перед началом тестов (чтобы убедиться, что нет в процессах никаких висящих драйверов)

WindowsUtils.killByName("chromedriver.exe"); //убивает хромдрайвер, если он есть в процессах

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

 

Вот тут можно почитать о других не самых известных фишках Selenium

Software-Testing.Ru

Чтение документации как средство от граблей и костылей.

se

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

securityservПоиск элемента по тексту при помощи CSS. У меня за время использования Selenium скопилась целая библиотека книг, интересных статей и видео о использовании WebDriver и во многих из них, причем достаточно свежих, упоминается CSS локатор в стиле a:contains(‘text’) . То есть люди переписывают этот локатор, даже не проверяя, работает ли он. Но данный локатор не работает! В спецификации W3C можно увидеть пункт с довольно зловещим номером.

666

Особенно мне понравилось недавно просмотренное видео, где хвалили один из новомодных курсов по автоматизации, где радостный участник рассказал, что его научили применению локаторов,  в числе прочего использовать и такой локатор.

securityservБудем честны, хоть раз каждый из нас использовал паузу в стиле Thread.sleep(), а кто не признается, тот и сейчас пользуется 🙂 Как я уже писал – это плохой способ ждать чего-либо и не стоит этим злоупотреблять в своем коде, но если уж решили использовать, то не нужно писать развесистый куст с Thread.sleep() и перехватом InterruptedException. Библиотека WebDriver содержит возможность подождать, впрочем внутри там тот же самый sleep()

new Actions(driver).pause(1000).perform();

— так короче и не бросается в глаза использование слипов.

Стоит отметить, что при написании этой статьи, я обнаружил, что при использовании этого метода в паре со слушателем событий вебдрайвера и хромдрайвером, в глубинах вебдрайвера падает исключение UnsupportedOperation Exception, которое вверх не пробрасывается, но отлавливается упомянутым слушателем. Пауза при этом работает, видимо апи Actions еще не полностью реализовано в хромдрайвере. Но это не мешает использовать паузу.

 

securityservWebElement не хранит свойств элемента страницы. Работая с Java, мы привыкли, что полноценный, сформированный объект хранит в себе свои поля, предоставляет публичные геттеры и не зависит от других объектов. Скажем ArrayList со значениями, полученными откуда угодно, доступен независимо от текущей доступности источника данных. Но WebElement  для запроса тега, текста или других параметров использует запрос к вебдрайверу и если браузер закрыт или изменилась страница, был переход на другой УРЛ, то падает исключение.

Пример:

WebElement element = driver.findElement(By.id(“one”));
driver.get(“http://anotherPage.com”);
System.out.println(element.getText()); // тут падает StaleElementReferenceException так как элемента нет на странице.

Казалось бы, объект element создан и все знает о своем состоянии, но по факту это не так, любые параметры получаются запросом к драйверу. И в коде

if (element.isDisplayed()) {
    System.out.println(element.isDisplayed());
}

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

securityservУ вебдрайвера есть АПИ работы с логами браузера, что порой очень удобно – можно после каких-то действий проверить нет ли ошибок в консоли. Но работает это апи довольно неочевидным образом – при запросе логов они удаляются (для запрошенного уровня browser, driver и так далее) и заново их получить (те же самые логи) вы не можете!

То есть допустим два простых метода получения ошибок браузера и проверки наличия таких ошибок

public List consoleErrors() {
        return driver.manage().logs().get("browser").filter(Level.SEVERE);
    }

public boolean isConsoleErrorsExists(){
        return consoleErrors().size() > 0;
}

Если мы воспользуемся методом isConsoleErrorsExists() то даже если ошибки и были, мы их уже не увидим – логов больше нет, мы их запросили внутри метода, вызвав consoleErrors(). Используйте логи сразу при получении, второго шанса не будет!

securityservПри использовании поиска по локатору className будут найдены не только все элементы у которых класс точно равен искомому, но и все те, где искомое лишь один из классов элемента. То есть при поиске By.className(«link») будут найдены и элементы, где class=’link’, и все те, кто содержит link, например class = ‘link b-sethome__link i-bem link_js_inited’. Это нужно учитывать.

securityservИногда нужно проверять какие то ссылки со страницы, или новые урл на предмет доступности, я в свое время накидал простенький код для проверок с использованием средств Java. Но в библиотеке Selenium уже есть очень полезный в подобных случаях класс UrlChecker, позволяющий проверять доступность ссылок, а конкретнее он ждет в течение заданного времени, что УРЛ вернет код ответа 200, означающий доступность страницы. Это можно сделать так

 new UrlChecker().waitUntilAvailable(timeOutInSeconds, TimeUnit.SECONDS, new URL(“http://site.com”));
// останется только обработать TimeOutException

securityservКак то приходилось писать костылек по скроллированию большой интерактивной страницы в самый низ для проверок, но оказалось и тут все написано до нас! Есть очень удобный интерфейс Locatable, который позволяет получить координаты элемента относительно окна, страницы или всего экрана. А самое важное, что для этого происходит автоматически скроллирование к этому элементу, чтобы он стал видимым. Применяется вот так

((Locatable) element).getCoordinates().inViewPort();

Магия происходит именно в методе inViewPort(); согласно спецификации вместо него с тем же эффектом можно вызвать и onScreen() – но он пока не реализован в драйверах, в отличие от inViewPort().

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

И всех с прошедшими праздниками!

 Software-Testing.Ru

Отладка Selenium тестов в jshell, не забывая про Idea

java9

Многие уже в курсе, что с Java 9 к нам пришла и джава-консоль jshell, позволяющая быстро выполнять джава-код без лишних телодвижений. Например для извечного «Hello, world!» не нужно создавать класс, а в нем метод main, просто пишем в консоли System.out.println(«Hello, world!»).

Jshell как уже говорилось идет в составе 9 Java, при условии верно прописанных переменных окружения просто пишем в командной строке jshell  и немного ждем, запуск не мгновенный.

По умолчанию в jshell импортировано несколько пакетов, которые можно посмотреть командой /imports, все остальное при необходимости нужно импортировать самим.

imp

Как видим, можно проводить расчеты, работать со стримами и файлами, по-быстрому проверить какой то код не создавая классов и не обрабатывая исключений, к примеру можно вывести содержимое файла не обрабатывая FileNotFoundException

file

Но ближе к делу! Консоль можно использовать для отладки ваших тестов на  Selenium потому что она позволяет:

  • не писать излишнего кода, не создавать классов, не смотреть пока на исключения
  • в случае падения исключения продолжать выполнение, предприняв необходимые действия (изменить урл, локатор  и т.п.) при этом браузер жив и продолжает работать
  • добавлять новые переменные в код
  • в результате вывести весь ваш код командой /list  и скопировать его в ваш тест
  • демонстрировать простые действия или обучать работе с Selenium

Для начала нам нужна сама библиотека Selenium, поэтому качаем свежую версию стендалон-сервера и кладем его в нужную папку, у меня это та же папка где и драйвера браузеров лежат. Потом указываем нашей jshell где искать библиотеку, есть несколько способов сделать это, я указываю при запуске прямо в инструменте Выполнить Windows, так как он запоминает параметр и больше вводить не нужно

jshell —class-path C:\drivers\selenium-server-standalone-3.8.1.jar

path

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

chrome

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

Завершив, выводим весь наш сценарий

list

И на сладкое! Еще удобнее работать с jshell в среде разработки Intellij Idea! Просто открываем терминал Идеи, запускаем там jshell с нужными настройками и готово. Тут есть большое удобство в том, что можно тут же код копировать из Идеи в консоль и обратно.

idea

Не забывайте почитать документацию по jshell, там много интересного.

P.S. Я не предлагаю вам отлаживать или писать тесты именно так, скорее продемонстрировал еще одну возможность, которая, на мой взгляд, может быть удобна в определенных ситуациях.


Software-Testing.Ru

 

 

 

 

 

 

 

Ожидания в стиле Java 8

083402_kx1b2e8athumbnail

На мысль навел просмотр видео, в котором Сергей Король справедливо напомнил, что многие из нас «упираются» собственно в средства Селениума и не используют всю силу благословенной Java. Уже вышла 9 версия, а многие еще недостаточно освоили 8, часто ли вы используете в проекте лямбды, стримы, функциональные интерфейсы?

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

Сначала пишем небольшой enum, в котором используем все наиболее часто применяемые ожидания. Вот весь код, далее обсудим:

import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;

import java.util.function.Function;

public enum WaitConditions {
   visible   (ExpectedConditions::visibilityOfElementLocated),
   exist     (ExpectedConditions::presenceOfAllElementsLocatedBy),
   clickable (ExpectedConditions::elementToBeClickable),
   invisible (ExpectedConditions::invisibilityOfElementLocated);

   WaitConditions(Function<By, ExpectedCondition> type) {
      this.type = type;
   }

   public Function<By, ExpectedCondition> getType() {
      return type;
   }

   private final Function<By, ExpectedCondition> type;
}

Тип нашего enum  это функциональный интерфейс Function, который получает на вход By (наш локатор) и возвращает ExpectedCondition то есть те самые условия ожидания.

 visible (ExpectedConditions::visibilityOfElementLocated),
 exist (ExpectedConditions::presenceOfAllElementsLocatedBy),
 clickable (ExpectedConditions::elementToBeClickable),
 invisible (ExpectedConditions::invisibilityOfElementLocated);

Тут мы собственно перечисляем нужные значения и те методы, на которые их нужно завязать.

private final Function<By, ExpectedCondition> type;

Обратите внимание на ? — это необходимо для использования invisibilityOfElementLocated, так как он в отличие от остальных методов возвращает ExpectedCondition.

С enum разобрались, теперь посмотрим на метод, который будет использовать его. Сразу оговорюсь,  я использую возвращаемое значение boolean, а не кидаю исключение выше, но это мой стиль работы, вы можете модифицировать метод:

public boolean waitFor(By locator, WaitConditions conditions) {
   WebDriverWait wait = new WebDriverWait(driver, defaulTime);
   try {
      wait.until(conditions.getType().apply(locator));
      return true;
   } catch (TimeoutException ex) {
      //делаем скриншот, логируем и  т.д.
      return false;
   }
}

defaultTime — это переменная int, сколько конкретно секунд ждать по умолчанию, при желании ее можно менять, driver это конечно же наш WebDriver. Вот тут все происходит wait.until(conditions.getType().apply(locator)); — наш локатор используется для ожидания соответствующего типа.

Не так много кода мы написали, но все это просто и лаконично позволит нам в коде писать вызовы наших ожиданий в стиле

waitFor(By.id("loginButton"), visible);
или
waitFor(submitButton, clickable);

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

Software-Testing.Ru

 

Headless Chrome вместе с Selenium WebDriver — для нетерпеливых

google_now_in_chrome_canary_release_nemmfq

Кто еще не в курсе, начиная с 59 версии в браузер Хром будет введена возможность запуска в headless-режиме, то есть без создания визуального окна браузера. Это позволит прогонять тесты быстрее (теоретически) и с меньшими затратами ресурсов, а главное — позволит запускать тесты на системах без графической составляющей. Не беспокойтесь — возможность делать скриншоты никак не пострадает.

Естественно, кроме самого браузера необходимо дождаться и новой версии chrome driver (текущая 2.29). Но если ждать не хочется, а хочется уже сейчас посмотреть и попробовать, то вот простой рецепт (проверялось для chrome driver 2.29 на Windows 10)

Скачиваем и устанавливаем Chrome Canary, который поддерживает все новые функции будущей версии Хрома. Не беспокойтесь, установка идет в отдельную папку по умолчанию и ваш родной текущий Хром браузер никак не пострадает. Сразу запоминаем или копируем путь установки.

Стандартно указываем путь к нашему драйверу хром

System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/vendors/chromedriver.exe");

в данном случае, у меня драйвер лежит в папке проекта, в директории vendors.

Далее указываем хром проперти для использования headless режима

ChromeOptions options = new ChromeOptions();
options.setBinary("C:\\Users\\admin\\AppData\\Local\\Google\\Chrome SxS\\Application\\chrome.exe");
options.addArguments("--headless");

в методе setBinary мы указываем путь к расположению нашего Chrome Canary, ну и гвоздем программы устанавливаем аргумент —headless, который говорит сам за себя.

далее, опять же по стандарту, просто создаем объект браузера

driver = new ChromeDriver(options);

Можно запускать!

Единственный момент, который я сразу обнаружил — невидимое окно браузера всего размером 800х600, видно по скриншотам. Кому то может это и не важно, а у нас приложение меняет некоторые элементы в зависимости от размеров окна. Поэтому, нужный размер окна устанавливаем вот так

options.addArguments("window-size=1800x900");

где 1800х900 это размер, нужный вам.

Родные методы driver.manage().window().maximize(); или driver.manage().window().setSize(); тут не сработают, так как chrome driver все еще 2.29  и видимо пока не может использовать эти операции с headless браузером.

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

Software-Testing.Ru

Не xpath единым, или использование CSS

logo14

Да, я в курсе, что в сети полно блогов и туториалов по использованию CSS, но у них всех есть общая проблема — они стремятся рассказать о всех функциях и вариантах использования и не говорят когда именно использовать именно CSS локаторы. В итоге начинающий не знает за что схватиться и не понимает когда использовать CSS, а когда Xpath.

По своему опыту убежден, что все функции не нужны для постоянной работы в 99% случаев ни в XPATH, ни в CSS. Основные полезные функции xpath я уже описывал, теперь обсудим Css и когда какие локаторы использовать.

  1. Класс. В CSS очень удобно использовать класс элемента (если он уникален), применяя просто знак «.» (точка). Например для элемента с тегом <div idc__dX351« classfbBtn btn-grflat btn font_button« enabledenabled«> можно указать локатор «.fbBtn«. Да, все так просто. Если элементов с таким словом в классе несколько, то можно указать еще один из классов в строке, например «.fbBtn.btn-grflat«. Тут нужно понимать, что все слова, перечисленные через пробел в теге class, это все классы CSS назначенные данному элементу, то есть найти его можно по любому из них, но конечно fbBtn бросается в глаза как явно уникальное слово в потоке остальных.
  2. id в случаях, когда он уникален, что в наше время роскошь, то просто используется запись в стиле #myid. Коротко и просто.
  3. Значения атрибутов. Легко найти элемент по значению его атрибута, если нет уникального класса или id. <input idc__db231_input« classinput__nativeinput« typetext«  placeholderЭл. почта« nameemail« value=»»/>  Как видим, мало что уникально в описании элемента, вполне оправдано использовать атрибут name и его значение. Получаем input[name=’email’]  Согласитесь, достаточно коротко и понятно, квадратные скобки, как и у xpath означают фильтр, если условий несколько то можно использовать запись в стиле  input[name=’email’][placeholder=’Эл. почта] — в данном случае, это тот же элемент.
  4. Поиск по частичному совпадению значения атрибута. А конкретнее, использования аналогов функций СОДЕРЖИТ, НАЧИНАЕТСЯ С, ОКАНЧИВАЕТСЯ НА. Вышеуказанный инпут можно найти и так input[placeholder*=’Эл’], запись «*=» означает СОДЕРЖИТ, аналог contains() в xpath. В данном случае ее можно заменить функцией НАЧИНАЕТСЯ С вот так input[placeholder^=’Эл’], то есть «^=» является аналогом starts-with в xpath. И, наконец, input[placeholder$=’почта’], дает нам выражение «$=» означающее ОКАНЧИВАЕТСЯ НА, не имеющее аналогов в xpath.
  5. Непосредственный наследник и потомок. Для того, чтобы добраться до непосредственного наследника (аналог child в xpath) нужно использовать запись в стиле div>div
    <div idc__da135« classsignInUsingEmailLbl font_title view« enabledenabled«>
              <div classlbl-cnt«>Входdiv>
    Тут, для того чтобы получить div с текстом Вход, можно использовать такую запись div.signInUsingEmailLbl>div  то есть сначала мы находим родителя по его классу (signInUsingEmailLbl), затем его «ребенка» с тегом div.
    <div idc__da135« classsignInUsingEmailLbl font_title view« enabledenabled«>
              <div classlbl-cnt«>Входdiv>

                   <div classex-cnt«>div>

    Чтобы получить потомка любой вложенности (аналог // или descendant в xpath) нужно просто поставить пробел! Чтобы найти элемент с классом «ex-cnt» в примере выше, используем div.signInUsingEmailLbl  div.ex-cnt то есть опять же находим родителя, а потом любого потомка с классом ex-cnt.

    securityservНа мой взгляд — это самые полезные функции  в CSS, которые легко запомнить, короткие в написании и понимании. По поводу скорости CSS по сравнению с Xpath я уже говорил — разница незначительна, но конечно чем короче в написании локатор, тем лучше. Минусом СSS является то, что поиск идет только сверху вниз, то есть мы не можем найти предка, только потомков (вспоминаем в xpath функции ancestor и parent).

Итак, используем CSS если:

  • есть уникальный класс (.fbBtn) или id
  • есть возможность определить элемент по атрибуту или его части (input*=’email’)
  • есть возможность определить элемент более короткой записью чем в xpath

В других случаях, когда нам нужен предок, нужно движение по иерархии вверх (а иногда вверх-вниз!), нужны сложные условия для получения элемента, лучше использовать xpath. Про логику и правила нахождения локаторов в сложных случаях верстки напишу в другой статье.


Software-Testing.Ru