Русский
Русский
English
Статистика
Реклама

Selenium

Перевод Запуск тестов Selenium в Jenkins

01.05.2021 16:18:08 | Автор: admin
В наши дни понятие DevOps у всех на слуху. Это организационный подход, широко используемый для ускорения разработки и развёртывания приложений. Организации внедряют у себя практики DevOps, так как они обещают дать тем, кто их использует, всё лучшее, что существует в мире разработки ПО, причём на всех этапах работы от планирования и тестирования, до развёртывания и мониторинга проектов. В реализации практик DevOps важную роль играют CI/CD-инструменты вроде Jenkins. А интеграция Jenkins с Selenium значительно облегчает процесс автоматизации Selenium-тестов.



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

Что такое Jenkins?


Jenkins это опенсорсный DevOps-инструмент, который пользуется популярностью в деле организации процессов непрерывной интеграции и непрерывной доставки программного обеспечения. Это платформонезависимое приложение, написанное на Java. Оно представляет собой систему для сборки проектов. В частности, с его помощью собирают проекты из исходного кода, запускают модульные тесты, рассылают отчёты о сборках соответствующим членам команды разработчиков.

Подробнее о Jenkins можно почитать здесь.

Что такое Selenium?


Selenium это опенсорсный инструмент, который широко используется для автоматизации тестирования веб-приложений. Пользоваться им просто, существуют форумы по Selenium, куда может обратиться тот, кто столкнулся с какими-то проблемами. Благодаря этому Selenium обрёл определённую популярность в кругах тестировщиков ПО. В состав Selenium входят четыре основных компонента: Selenium IDE, Selenium RC, Selenium WebDriver и Selenium Grid. Они созданы в расчёте на решение различных задач. Selenium даёт тому, кто им пользуется, возможности по кросс-браузерному и параллельному тестированию веб-приложений. Это позволяет тестировщикам выполнять испытания приложений в разных операционных системах и браузерах, что, в частности, даёт им возможность проверить совместимость веб-проекта с различными браузерами.

Если вас интересуют подробности о Selenium посмотрите этот материал.

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

Установка, настройка и запуск Jenkins


Шаг 1 Для того чтобы загрузить Jenkins можно воспользоваться соответствующей ссылкой на официальном сайте проекта. Нас, в частности, интересует .war-файл Jenkins.

Шаг 2 Загрузим файл jenkins.war и сохраним его в выбранную директорию.

Шаг 3 Откроем окно командной строки и перейдём в папку, в которой хранится загруженный .war-файл.

Шаг 4 Выполним команду java -jar Jenkins.war. Будет запущен сервер Jenkins.

Шаг 5 Обычно Jenkins по умолчанию запускается на порте 8080. Если этим портом уже пользуется какой-то другой сервис, можно указать при запуске Jenkins другой порт, воспользовавшись командой такого вида:

java -jar jenkins.war --httpPort=8081

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

Настройка порта для Jenkins

При первом запуске Jenkins будет сгенерирован пароль, который выводится в консоли, в выделенной области, показанной ниже. Пароль на изображении скрыт. Он используется при установке Jenkins.

Пароль

В консоли, после успешного завершения установки, можно будет увидеть сообщение Jenkins is fully up and running.

Шаг 6 Запустим браузер и перейдём к localhost. Как уже говорилось, по умолчанию Jenkins запускается на порте 8080. В моём случае порт был изменён на 8081, именно к этому порту localhost мне и нужно подключиться.

После этого откроется страница настроек Jenkins. Она используется для создания административной учётной записи.

Шаг 7 Введём в соответствующее поле пароль, который ранее был показан в консоли, и нажмём на кнопку Continue для продолжения настройки системы.

Окно ввода пароля

Шаг 8 После ввода пароля система предложит нам установить плагины.

Предложение об установке плагинов

Если вы не знаете точно о том, какие именно плагины вам понадобятся можете выбрать вариант Install suggested plugins. Это приведёт к установке набора наиболее часто используемых плагинов. Но если вы знаете о том, что именно вам нужно, основываясь, например, на требованиях к проекту, вы можете выбрать вариант Select plugins to install и самостоятельно выбрать плагины для установки.

После этого плагины будут загружены и установлены.

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

Создание учётной записи администратора

Существует множество способов интеграции Jenkins с Selenium WebDriver. Некоторые из них мы рассмотрим ниже. Разные способы интеграции этих систем применимы в различных условиях.

Первый метод интеграции Jenkins с Selenium


Шаг 1 В панели управления Jenkins щёлкнем по значку New Item для создания нового проекта. Укажем имя проекта и выберем вариант Freestyle Project. После этого нажмём OK.

Начало создания нового проекта

Настройка свойств проекта

Шаг 2 На вкладке свойств проекта General введём, в поле Description, описание проекта.

Ввод описания проекта

Шаг 3 На вкладке Source Code Management выберем None в группе параметров Source Code Management.

Выбор системы управления исходным кодом

Шаг 4 Jenkins позволяет планировать задания по сборке проектов. Время запуска заданий указывают в таком формате:

MINUTE HOUR DOM MONTH DOW

Смысл этих сокращений раскрыт в следующей таблице

Сокращённое наименование Описание
MINUTE Минуты в пределах часа (0 59)
HOUR Часы в пределах дня (0 23)
DOM День месяца (1 31)
MONTH Месяц (1 12)
DOW День недели (0 7), где воскресенье может быть представлено значениями 0 и 7

Шаг 5 Создадим проект, который будем применять для запуска тестов с использованием Selenium WebDriver и TestNG.

Вот код на Java:

package Pages;import static org.testng.Assert.assertEquals;import java.util.concurrent.TimeUnit;import org.openqa.selenium.By;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.testng.annotations.AfterTest;import org.testng.annotations.BeforeTest;import org.testng.annotations.Test;public class LoginPage {WebDriver driver;@BeforeTestpublic void setUp() {System.setProperty("webdriver.chrome.driver", "C:\\Users\\Shalini\\Downloads\\chrom86_driver\\chromedriver.exe");driver = new ChromeDriver();}public void login() {String login_url = "https://opensource-demo.orangehrmlive.com/";driver.get(login_url);driver.manage().window().maximize();driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);driver.findElement(By.id("txtUsername")).sendKeys("Admin");driver.findElement(By.id("txtPassword")).sendKeys("admin123");System.out.println(driver.getTitle());}@Testpublic void dashboard() {driver.findElement(By.id("menu_dashboard_index")).click();String textPresent = driver.findElement(By.xpath("//*[@id=\"content\"]/div/div[1]/h1")).getText();String textToBePresent = "DashBoard";assertEquals(textPresent, textToBePresent);}@AfterTestpublic void tearDown() {driver.quit();}}

Шаг 6 Создадим файл TestNG.xml:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><suite name="TestSuite"><test name="LoginTest"><groups><run><include name="DemoTest"/></run></groups><classes><class name="Pages.LoginPage"></class></classes></test></suite>

Шаг 7 В папке проекта добавим зависимости. Создадим .bat-файл со следующим содержимым:

ava cp bin;lib/* org.testng.TestNG TestNG.xml

Тут надо указать точное имя файла TestNG.xml, созданного ранее.

Шаг 8 Выберем в панели управления Jenkins проект, который мы создали в самом начале работы. Щёлкнем по Configure. На вкладке General щёлкнем по Advanced и установим флажок Use custom workplace. Введём в поле Directory путь к папке проекта.

Ввод пути к папке проекта

Шаг 9 На вкладке Build откроем выпадающий список Add Build Step и выберем Execute Windows batch Command. Введём в него имя .bat-файла, созданного на шаге 7.

Ввод сведений о .bat-файле

Шаг 10 Щёлкнем по кнопке Apply, потом по кнопке Save для того чтобы сохранить изменения, внесённые в настройки Jenkins-проекта. Теперь щёлкнем по кнопке Build Now, после чего можно будет понаблюдать за процессом выполнения тестов.

Запуск сборки проекта

Второй метод интеграции Jenkins с Selenium


Второй подход к автоматизации Selenium-тестов в Jenkins предусматривает использование Maven. Но прежде чем мы это обсудим, предлагаю немного поговорить о Maven, и о том, почему этот фреймворк пользуется популярностью при автоматизации тестов в Jenkins.

Что такое Maven?


Maven это инструмент для автоматизации сборки проектов, который позволяет организовать добавление в проект зависимостей и управление ими с использованием файла pom.xml. Это позволяет многократно использовать одни и те же зависимости во множестве проектов. При добавлении зависимости в файл pom.xml эта зависимость автоматически загружается и добавляется в проект, что избавляет разработчика от необходимости вручную добавлять в проекты зависимости в виде JAR-файлов.

Совместное использование Maven, Jenkins и Selenium WebDriver даёт возможность построения DevOps-модели, реализующей механизм непрерывной интеграции программных проектов.

Установка Maven


Шаг 1 Загрузим дистрибутив Maven с официального сайта проекта.

Шаг 2 Добавим в состав системных переменных MAVEN_HOME переменную, в которой содержится путь к домашней директории Maven.

Системная переменная MAVEN_HOME

Шаг 3 Внесём в переменную Path путь к директории bin, содержащейся в домашней директории Maven.


Настройка переменной Path

Шаг 4 Для того чтобы проверить правильность установки Maven откроем окно командной строки и попробуем выполнить команду mvn version.

Проверка правильности установки Maven

Шаг 5 Создадим Maven-проект и добавим в файл pom.xml соответствующие зависимости:

<project xmlns="http://personeltest.ru/away/maven.apache.org/POM/4.0.0" xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://personeltest.ru/away/maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>demoProject</groupId><artifactId>demoProject</artifactId><version>0.0.1-SNAPSHOT</version><build><sourceDirectory>src</sourceDirectory><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version><configuration><sоurce>1.8</sоurce><target>1.8</target></configuration></plugin></plugins></build><dependencies><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>3.141.59</version></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.3.0</version><scope>test</scope></dependency></dependencies></project>

Вот Java-код:

package WebDriverProject;import org.junit.Assert;import org.openqa.selenium.By;import org.openqa.selenium.WebDriver;import org.openqa.selenium.firefox.FirefoxDriver;public class LoginClass {WebDriver driver;@BeforeTestpublic void setup() {System.setProperty("WebDriver.gecko.driver", "C:\\Users\\shalini\\Downloads\\geckodriver-v0.26.0-win64\\geckodriver.exe");driver = new FirefoxDriver(); //Инициализация WebDriver}@Testpublic void loginTest() {driver.get("https://opensource-demo.orangehrmlive.com/"); //Определение URLString pageTitle = driver.getTitle(); //get the title of the webpageSystem.out.println("The title of this page is ===> " + pageTitle);Assert.assertEquals("OrangeHRM", pageTitle); //Проверка заголовка страницыdriver.findElement(By.id("txtUsername")).clear(); //Очистить поле перед вводом значенийdriver.findElement(By.id("txtUsername")).sendKeys("Admin"); //Ввести имя пользователяdriver.findElement(By.id("txtPassword")).clear();driver.findElement(By.id("txtPassword")).sendKeys("admin123"); //Ввести парольdriver.findElement(By.id("btnLogin")).click(); //Щёлкнуть по кнопке LoginSystem.out.println(Successfully logged in );}@AfterTestpublic void teardown() {driver.quit();}}

Вот XML-файл с настройками тестов:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><suite name="TestSuite"><test name="LoginTest"><groups><run><include name="DemoTest"/></run></groups><classes><class name=" WebDriverProject.LoginClass"></class></classes></test></suite>

Интеграция Selenium и Jenkins с помощью Maven


Только что мы говорили об интеграции Jenkins с Selenium WebDriver. То, что получилось, лучше всего использовать для автоматизации Selenium-тестов. Интеграция Jenkins с Selenium WebDriver даёт разработчику надёжную систему кросс-браузерного тестирования проектов. Здесь мы поговорим об интеграции Jenkins и Selenium с помощью Maven.

Шаг 1 Запустим сервер Jenkins.

Шаг 2 Откроем браузер и перейдём на localhost, использовав порт, на котором работает Jenkins.

Открытие панели управления Jenkins в браузере

Шаг 3 Щёлкнем по кнопке New Item в панели управления

Кнопка New Item

Шаг 4 Введём имя проекта и, в качестве типа проекта, выберем Maven project.

Ввод имени проекта и выбор его типа

Шаг 5 Нажмём на кнопку OK. После этого можно будет увидеть, как в панели инструментов появилось новое задание.

Новый проект в панели управления

Шаг 6 Выберем проект и нажмём на кнопку Configure.

Переход к настройке проекта

Шаг 7 На вкладке Build, в поле Root POM, введём полный путь к файлу pom.xml. В поле Goals and options введём clean test.

Настройка параметров сборки проекта

Шаг 8 Щёлкнем Apply, а затем Save.

Шаг 9 Нажмём на кнопку Build Now.

Запуск сборки проекта

Шаг 10 Будет начата сборка проекта, а после её завершения появится соответствующее сообщение. Для того чтобы посмотреть полный журнал нужно щёлкнуть по кнопке Console Output.

Просмотр журнала сборки

Итоги


Мы поговорили о том, почему проект Jenkins пользуется серьёзной популярностью, и о том, как интегрировать его с Selenium WebDriver ради эффективного выполнения тестов и достижения целей непрерывной интеграции программных проектов. Использование Jenkins для автоматизации запуска тестов позволяет разработчикам экономить время, а результаты выполнения тестов можно проанализировать, просмотрев соответствующие лог-файлы. Такой подход позволяет организовать полный цикл разработки ПО разработку, развёртывание, тестирование, мониторинг, выпуск в продакшн. Jenkins поддерживает множество различных плагинов, предназначенных для решения самых разных задач, встающих перед разработчиками. Система, кроме того, умеет сообщать заинтересованным лицам о том, успешно ли прошла сборка проекта.

Надеюсь, теперь вы без труда сможете интегрировать Jenkins с Selenium WebDriver.

Пользуетесь ли вы Jenkins и Selenium WebDriver?

Подробнее..

Cypress и его место в нашей тестовой пирамиде

18.05.2021 08:17:48 | Автор: admin

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

Введение в Cypress

Если отбросить капитанское определение, что Cypress это JavaScript-фреймворк для тестирования, то важно отметить, что при работе с ним мы видим на экране браузер. Он не обязательно открыт, он может быть headless, но он есть, и он открывает особое приложение самого Cypress, которое состоит из нескольких фреймов: в одном фрейме открывается продукт, который мы тестируем, в другом фрейме запускаются тесты. Код тестов пишется на JavaScript, поэтому они могут выполняться непосредственно в браузере ведь это нативный для него язык.

Так с помощью JavaScript API производятся все манипуляции, которые делаются в тестах, то есть заполнение форм, клики и тому подобное.

Преимущества Cypress

Нет Selenium WebDriver

Очевидное отличие Cypress от тех библиотек и фреймворков, которые мы использовали раньше это отсутствие основного действующего элемента, Selenium.

Selenium WebDriver это third-party сервис на Java, который обращается к браузеру по WebDriver протоколу. Это накладывает ограничения на работу с браузером в рамках протокола. Сетевое взаимодействие также вносит свой вклад во время выполнения тестов.

Изначально Selenium был создан не специально для тестов, а как общий инструмент автоматизации для браузера. Cypress, в отличие от него, сфокусирован на решении конкретной задачи, а именно, на создании end-to-end (е2е) тестов для интерфейса web-приложений.

Все в одном

Cypress не нужно собирать из кусочков он принес все достаточно современные "батарейки" с собой:

  • Синтаксис BDD (унаследовано из Mocha): describe(), context(), it().
    А также хуки: before(), beforeEach().
    Использовать такой DSL привычно для тех, кто уже писал юнит-тесты на JavaScript.

  • Библиотека ассертов (унаследовано из Chai). Например:
    expect(name).to.not.equal("Jane") ожидание того, что элемент не существует это не то же самое, что ожидание неудачи при проверке существования элемента. Если элемента нет, то это хорошо, это не нужно перепроверять, а нужно идти дальше.
    Такую задачу должен решать тестовый фреймворк, и этого нам очень не хватало в старой самописной библиотеке, при использовании которой многое ложится на плечи разработчика теста.

  • Перехват, отслеживание (spy) и подмена (mock) запросов браузера к бэкенду.

Development experience

Главное преимущество Cypress это отличный development experience. Написать первый тест для своего проекта (неважно, на каком языке написан сам проект) можно минут за 10. Потребуется добавить одну зависимость в package.json (npm install cypress), прочитать документацию про то, куда складывать файлы (cypress/integration/login.spec.js), и написать код в 5 строчек:

describe('Login', () => {it('should log in with credentials', () => {cy.visit('/login');cy.get('[name=login_name]').type(Cypress.env('login'));cy.get('[name=passwd]').type(Cypress.env('password'));cy.get('[name=send]').click();cy.get('.main-header').should('be.visible');});});

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

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

Приятной мелочью является то, что каждый cy.get() убеждается, что страница загрузилась, и делает несколько попыток, чтобы найти элемент. С каждым годом интерфейсы веб-приложений становятся все сложнее. Результирующий HTML формируется не на стороне сервера, а на стороне браузера. Делается это асинхронно и с использованием различных библиотек компонентов. В какой момент тот или иной элемент интерфейса появится на экране, сказать уже становится сложнее.

Одна из Best Practices говорит, что не нужно никогда писать таймаут типа "подождать 2 секунды". Абсолютно все таймауты должны ждать чего-то осязаемого, например, окончания Ajax-запроса. Можно подписаться на событие, которое случается в коде продукта. Например, когда нам через веб-сокет прилетает событие с бэкенда, то срабатывает определенный listener на фронтенде.

Вся документация Cypress и Best Practices находятся на одном сайте docs.cypress.io хотелось бы отдельно отметить высокое качество этой документации, а также мастер классов, которые команда разработки Cypress проводит и публикует в открытом доступе.

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

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

Тестовая пирамида

Когда говорят про тестовую пирамиду, то обычно приводят в пример анти-паттерн "перевернутая пирамида" или "стаканчик мороженого". То есть на нижнем уровне в таком примере количество юнит тестов стремится к нулю. Лично мне этот случай кажется невероятным для зрелого проекта: ведь в этом случае разработчики должны были полность отказаться писать самые простейшие тесты откуда тогда взялись сложные е2е тесты?

Как бы то ни было, к нам это не относится у нас несколько тысяч PHPUnit-тестов с покрытием около 12% строк кода.

В то же время у нас есть еще несколько тысяч е2е-тестов с Selenium, которые проверяют все возможные конфигурации продукта, занимают кучу времени (подмножество, запускаемое на каждый коммит, мы смогли оптимизировать до 40-60 минут), имеют довольно слабый уровень доверия (с вероятностью 30-40% тесты упадут, хотя коммит не содержит причины этого падения) и покрывают около 30% строк кода.

Получается, наше положение выглядит, как песочные часы нам не хватает среднего слоя в тестировании, где интеграционные тесты проверяют компоненты системы независимо друг от друга. Это горлышко песочных часов и хочется заполнить с помощью Cypress. При этом еще хочется что-то сделать с существующими е2е тестами, чтобы "заострить" вершину пирамиды. То есть, важный акцент здесь в том, что Cypress не является заместителем старого фреймворка: мы не хотим просто взять и переписать все тесты на Cypress иначе мы так и останемся на шарике мороженого. Цель тестов по-прежнему, проверять регрессию в продукте, но проверять на другом уровне, чтобы выполняться быстрее и получать результат раньше, а также быть легче в сопровождении.

Наш подход к написанию тестов

Проект, о котором идет речь, это контрольная панель Plesk. Она предоставляет пользователям интерфейс для управления хостингом веб сайтов. Функциональность панели доступна не только через UI, но и через API и CLI, которые используются для автоматизации.

Мы начали с того, что сделали следующие предположения:

  • Тесты на Cypress относятся чисто к UI. Мы не относим сюда тесты, у которых шаги выполняются через API или CLI.

  • Мы не проводим никакой дополнительной валидации, кроме той, что выполняется средствами UI. Например, если мы проверяем создание домена, то мы не отправляем запросы для проверки Web-сервера или DNS, мы считаем тест пройденным, если в UI появилось сообщение на зеленом фоне о том, что домен создан успешно. Такой подход избавляет нас от предварительной подготовки и написания тестовых сценариев.

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

Наш опыт работы с Cypress в сочетании с официальными рекомендациями привел нас к использованию следующего набора практик:

Сбрасывать состояние продукта

Мы сбрасываем состояние продукта до исходного перед запуском каждого набора тестов (Cypress рекомендует делать это перед запуском каждого теста, но мы используем облегченный вариант). Мы создаем дамп базы данных и восстанавливаем его перед прогоном каждого набора тестов (test suite / spec). Это занимает порядка 5 секунд.

before(cy.resetInstance);//=> test_helper --reset-instance//=> cat /var/lib/psa/dumps/snapshot.sql | mysql

Такой откат к прежнему состоянию может в общем случае привести Plesk в нерабочее состояние, т.к. помимо общей базы есть еще, например, базы отдельных утилит, а также бэкапы, которые хранятся в файловой системе. Но нас это устраивает, т.к. мы используем Cypress только для тестирования UI.

Использовать фикстуры

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

cy.setupData(subscription).as('subscription');//=> test_helper --setup-data < {domains: [{ id: 1, name: "example.com" }]}

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

Использовать прямые URL

Мы не используем навигацию и попадаем в нужные места UI по прямым URL-ам. Мы вызываем свою специальную команду login, которая создает сессию, а затем переходим прямо на нужную страницу.

beforeEach(() => {cy.login();cy.visit('/admin/my-profile/');});

В старом фреймворке мы бы использовали PageObject для входа в главное меню, а затем переходили бы из него к нужному элементу. Здесь же этого не требуется, так как мы тестируем только необходимую страницу. Единственное дублирование это команда login, но это не выглядит проблемой.

Фронтенд без бэкенда

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

const lastChecked = 'Jan 29, 2021 04:42 PM';cy.intercept('POST', '/admin/home/check-for-updates', {status: 'success',lastChecked,newVersion: null,whatsNewUrl: null,}).as('checkForUpdates');cy.get('[data-name="checkForUpdates"]').click();cy.wait('@checkForUpdates');cy.get('[data-name="lastCheckedDate"]').should('contain', lastChecked);

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

Стабильность тестов

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

Дожидаться выполнения Ajax-запроса

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

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

cy.intercept('POST', '/admin/customer/create').as('customerCreate');cy.get('[name=send]').click();cy.wait('@customerCreate');cy.get('.msg-box.msg-info').should('be.visible');

Дожидаться исчезновения индикатора загрузки

Кое-где в нашем интерфейсе фоновые операции, например, обновление списка, сопровождаются анимированным индикатором загрузки ("крутилкой"). Именно на таких страницах после окончания Ajax-запроса случается ошибка "element has been detached from the DOM" при попытке Cypress кликнуть на элементы списка. Поэтому мы добавляем после Ajax-запроса дополнительную строку, которая проверяет, что индикатор загрузки не виден.

cy.get('.ajax-loading').should('not.be.visible');

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

Ajax-запросы после окончания теста

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

До того момента, когда следующий тест сделает первый вызов "cy.visit()", предыдущая страница остается открытой и может отправлять Ajax-запросы (например, периодическое обновление), которые будут падать из-за ошибки авторизации (куки нет, сессии нет).

В качестве workaround можно переходить на пустую страницу, чтобы браузер сбрасывал все активные Ajax-запросы. Для этого добавляем в support/index.js

afterEach(() => {cy.window().then(win => {win.location.href = 'about:blank';});});

Первые результаты

За 3 человеко-месяца (3 итерации) мы получили следующие результаты:

  • 335 тестов на Cypress (разбиты на 84 спеки)

  • Пайплайн полностью выполняется за 35-40 минут, из которых сами тесты занимают 20 минут

  • Запуск пайплайна на каждый пулл-реквест в блокирующем режиме (то есть нельзя мержить без успешного прохождения тестов)

  • Уровень доверия выше 95% (то есть вероятность flaky падения ниже 5%)

  • Покрытие интерфейса 35% (ниже расскажу подробнее)

Пайплайн для запуска тестов

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

Как и для большинства наших задач, пайплайн запускается в Jenkins и хранится в Jenkinsfile вместе с кодом проекта.

Линейный пайплайн

В первом заходе получился простой линейный пайплайн.

Мы запускаем Docker-контейнер с Plesk в фоновом режиме и ждем, когда он будет доступен в локальной сети. Потом запускаем другой контейнер с Cypress и кодом тестов, он подключается к Plesk и выполняет все тесты, а мы ждем его завершения (не делаем detach).

Мы запускали тесты на машине с 12 ядрами, которая используется у нас для сборки Plesk и ряда его служб. В течении рабочего дня у нас бывает до 20-30 сборок. В результате Load Average достигал 20, и многие соседние процессы "вставали". Мы добавили ограничение на количество исполняемых сборок до 3-5. Но и этого оказалось недостаточно, соседи по железу продолжали жаловаться на нагрузку.

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

Пайплайн с параллельными шагами

Чтобы как-то ускорить процесс, мы решили воспользоваться Jenkins EC2 Fleet plugin, который предоставляет Jenkins slave ноду по требованию из Autoscaling Group в AWS и уничтожает неактивные ноды после некоторого простоя. Такой подход позволяет тратить деньги на аренду ресурсов только тогда, когда они необходимы.

Переход на spot-инстансы позволил нам существенно сэкономить: вместо $150 в месяц за ondemand c5.xlarge, мы стали тратить около $60 за c5.xlarge и более мощные c5.2xlarge.

А главное, мы можем делать столько одновременных запусков, сколько нам нужно.

Разворачивание новой ноды занимает порядка 2 минут. Мы сделали в пайплайне несколько шагов параллельными, чтобы за это время успеть собрать продукт и быть готовыми к его установке в Docker на новой ноде.

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

Пайплайн с параллельными тестами

В Cypress есть платная фича параллельный запуск тестов с помощью Cypress Dashboard. Но мы пошли простым и бесплатным путем перечисляем файлы с тестами при запуске контейнера, при этом первый запускает все четные файлы, второй все нечетные.

cypress run --spec $(find 'cypress/integration' -type f -name '*.js' | awk '(NR - ${RUNNER}) % ${TOTAL_RUNNERS} == 0' | tr '\n' ',')

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

В итоге мы укладываемся в приемлемые 35-40 минут для всего пайплайна, а время одной пачки тестов занимает примерно 20 минут.

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

Измерение URL coverage

В нашем проекте используется много различных языков программирования и анализ code coverage является больной темой, потому что сбор данных требует специальных билдов и объединения отчетов с нескольких конфигураций.

Для анализа тестового покрытия UI мы решили воспользоваться продуктовой аналитикой и сравнить данные, полученные от тестовых инсталляций, с данными от реальных пользователей. У нас уже был сервис, аналогичный Google Analytics, для сбора пользовательских метрик, а тестовые данные складывались отдельно и никем не использовались. Из множества метрик мы отфильтровали события о посещенных URL-ах (страницах) продукта, начали сохранять эти данные в удобном для нас виде в базу данных и составлять отчет по посещенным адресам.

По полученным данным, за счет всего автоматического и ручного тестирования внутри компании мы покрываем около 60% URL-ов, которые посещают реальные пользователи в течении месяца. Наши старые тесты покрывают около 25%, а новые тесты на Cypress уже достигли 35%.

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

Следующие шаги

Ускорить сборку Docker

Одна из проблем, над которой мы хотим поработать ускорение сборки контейнеров Docker. Как уже было сказано выше, мы создаем временный сервер в AWS (slave node) для каждой сборки Docker, и эта сборка на данный момент занимает в среднем 8 минут. Но поскольку каждый временный сервер новый, то мы совершенно не используем преимущества кэширования, а хотелось бы ими воспользоваться. Поэтому сейчас мы исследуем возможность использования BuildKit. Альтернативными решениями могут стать Kaniko или AWS CodeBuild.

Сократить количество е2е тестов

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

Основная идея: перенести все UI-тесты в Cypress, а в старом фреймворке оставить только CLI-тесты с детальными проверками. Поэтому для каждого UI-теста из старого фреймворка мы делаем следующее:

  1. Заменяем UI-шаги на CLI (если это возможно).

  2. Удаляем, если уже есть аналогичный тест с CLI.

  3. Если проверка возможна только через UI уносим ее в Cypress.

Например, при создании домена проверяется то, что он резолвится, и что на нем работают определенные скрипты. Эти проверки останутся только для создания домена через CLI. А тест на UI в Cypress будет проверять только появление сообщения о создании домена.

В результате мы избавимся от дублирования тестовых сценариев, сократим нагрузку на сервера с Selenium и в перспективе совсем от них избавимся, когда тестирование UI будет делать только Cypress.

Заключение

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

Cypress позволяет организовать как полноценные e2e тесты с прохождением пользовательских сценариев на реальном продукте, так и интеграционное тестирование отдельных компонентов фронтенда. Лучше заранее выбрать цель и договориться о правилах игры для всей команды.

Кроме того, стоит отметить, что запуск тестов потребляет довольно много ресурсов CPU, поэтому стоит заранее оценить количество параллельных запусков и спланировать масштабирование инфраструктуры.

Подробнее..

Перевод Функции XPath для динамических XPath в Selenium

16.12.2020 14:07:22 | Автор: admin

Будущих студентов курса "Java QA Automation Engineer" и всех интересующихся приглашаем посмотреть подарочное демо-занятие в формате открытого вебинара.

А также делимся переводом полезной статьи.


В данной статье рассматриваются примеры использования функций XPath для идентификации элементов.

Автоматизация взаимодействия с любым сайтом начинается с корректной идентификации объекта, над которым будет выполняться какая-либо операция. Как нам известно, легче всего идентифицировать элемент по таким атрибутам, как ID, Name, Link, Class, или любому другому уникальному атрибуту, доступному в теге, в котором находится элемент.

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

=>Руководство по Selenium для новичков см. здесь

Чему вы научитесь:[показать]

Обзор функций XPath

Обсудим сценарий, при котором атрибуты недоступны напрямую.

Постановка задачи

Как идентифицировать элемент, если такие локаторы, как ID, Name, Class и Link, недоступны в теге элемента?

Суть проблемы демонстрирует следующий пример:

Авторизация вTwitter

Как видно из скриншота выше, заголовок Log in to Twitter не имеет дополнительных атрибутов. Поэтому мы не можем использовать ни один из локаторов, таких как ID, Class, Link или Name, для идентификации этого элемента.

Плагин Firepath для Firefox сгенерировал следующий путь XPath:

//[@id=page-container]/div/div[1]/h1

Мы бы не рекомендовали использовать указанный выше путь XPath, поскольку структура страницы или id могут меняться динамически. Если все же использовать этот нестабильный XPath, вероятно, потребуется чаще его обновлять, что подразумевает лишнюю трату времени на поддержку. Это один из случаев, когда мы не можем использовать общее выражение XPath с такими локаторами, как ID, Class, Link или Name.

Решение

Идентификация элемента с помощью функций XPath по тексту

Поскольку у нас есть видимый текст Log in to Twitter, мы могли бы использовать следующие функции XPath для идентификации уникального элемента.

  1. contains() [по тексту]

  2. starts-with() [по тексту]

  3. text()

Функции XPath, такие как contains(), starts-with() и text(), при использовании с текстом Log in to Twitter помогут нам корректно идентифицировать элемент, после чего мы сможем произвести над ним дальнейшие операции.

1. Метод Contains()

Синтаксис.Чтобы найти на веб-странице элемент Log in to Twitter, воспользуйтесь одним из следующих выражений XPath на основе метода contains().

Поиск по тексту:

  • //h1[contains(text(), Login to)]

  • //h1[contains(text(), in to Twitter)]

Примечание. Наличие одного совпадающего узла свидетельствует об успешной идентификации веб-элемента.

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

Обратите внимание, что при указании всего текста Log in to Twitter с методом contains() элемент будет также идентифицирован корректно.

2. Метод starts-with()

Синтаксис.Чтобы найти на веб-странице элемент Log in to Twitter, используйте следующие выражения XPath на основе метода starts-with().

Поиск по тексту:

  • //h1[starts-with(text(),Log in)]

  • //h1[starts-with(text(),Log in to)]

Из приведенного выше примера видно, что XPath-функции starts-with() требуется по крайней мере первое слово (Log) видимого текста для однозначной идентификации элемента. Функция работает даже с неполным текстом, но он должен как минимум включать первое слово частично видимого текста.

Обратите внимание, что при использовании всего текста Log in to Twitter с методом starts-with() элемент будет также идентифицирован корректно.

Недействительный XPath для starts-with()://h1[starts-with(text(),in to Twitter)]

Примечание.Отсутствие совпадающих узлов свидетельствует о том, что элемент на веб-странице не был идентифицирован.

3. Метод text()

Синтаксис.Чтобы найти на веб-странице элемент Log in to Twitter, воспользуйтесь следующим выражением XPath на основе метода text().

В этом выражении мы указываем весь текст, содержащийся между открывающим тегом <h1> и закрывающим тегом </h1>. Если использовать функцию text() с частью текста, как в случае с методами contains() и starts-with(), то мы не сможем найти данный элемент.

Недействительное выражение Xpath для text():

Идентификация элемента с помощью функций XPath по атрибуту

Мы используем функции XPath (contains или starts-with) с атрибутом в тех случаях, когда в теге содержатся уникальные значения атрибутов. Доступ к атрибутам производится с помощью символа @.

Для лучшего понимания рассмотрим следующий пример:

Авторизация вGoogle

1. Метод Contains()

Синтаксис.Чтобы точно идентифицировать кнопку Im Feeling Lucky (Мне повезет) с помощью XPath-функции contains(), можно указать следующие атрибуты.

Вариант А поиск по значению атрибута Value

  • //input[contains(@value,Feeling)]

  • //input[contains(@value,Lucky)]

На скриншотах выше видно, что поиск по атрибуту Value слов Feeling или Lucky с помощью функции contains() позволяет однозначно идентифицировать данный элемент. Стоит отметить, что, даже если мы используем полное содержимое атрибута Value, мы сможем корректно идентифицировать элемент.

Вариант Б поиск по содержимому атрибута Name

//input[contains(@name=btnI)]

Неправильное использование функции XPath с атрибутом:

Нужно быть крайне внимательным при выборе атрибута для поиска с помощью методов contains() и starts-with(). Если значение атрибута не уникальное, мы не сможем однозначно идентифицировать элемент.

Если мы воспользуемся атрибутом type при идентификации кнопки I'm Feeling Lucky, то XPath не сработает.

Наличие двух совпадающих узлов свидетельствует о том, что нам не удалось корректно идентифицировать элемент. В данном случае значение атрибута type не является уникальным.

2. Метод starts-with()

Метод starts-with() в сочетании с атрибутом может пригодиться для поиска элементов, у которых начало атрибута постоянное, а окончание изменяется. Такой метод позволяет работать с объектами, динамически изменяющими значения своих атрибутов. Его также можно использовать для поиска однотипных элементов.

Перейдите на страницу авторизацииFacebook.

Изучите первое текстовое поле First Name (Имя) и второе текстовое поле Surname (Фамилия) формы авторизации.

Первое текстовое поле First Name идентифицировано.

Второе текстовое поле Surname идентифицировано.

В случае обоих текстовых полей, из которых состоит форма авторизации Facebook, первая часть атрибута id всегда остается неизменной.

First Name id="u02"

Surname id="u04"

Это тот случай, когда мы можем использовать атрибут вместе с функцией starts-with(), чтобы получить все элементы такого типа с атрибутом id. Обратите внимание, что мы рассматриваем эти два поля только для примера. На экране может быть больше полей с id, которые начинаются с u0.

Starts-with() [по атрибуту id]

//input[starts-with(@id,"u0")]

Важное примечание.Здесь мы использовали двойные кавычки вместо одинарных. Но одиночные кавычки тоже будут работать с методом starts-with.

11 найденных узлов указывают на то, что данное выражение XPath позволило идентифицировать все элементы, id которых начинается с u0. Вторая часть id (2 для имени, 4 для фамилии и т. д.) позволяет однозначно идентифицировать элемент.

Мы можем использовать атрибут с функцией starts-with там, где нам нужно собрать элементы похожего типа в список и динамически выбрать один из них, передавая аргумент в обобщенный метод, чтобы однозначно идентифицировать элемент.

На примере ниже показано использование функции starts-with.

Пример кода

/ <strong>Generic Method</strong> /

public void xpathLoc(String identifier){

//The below step identifies the element First Name uniquely when the argument is 2

WebElement E1=d1.findElement(By.xpath("//input[starts-with(@id,u0+identifier )]"));

E1.sendKeys(Test1); / This step enters the value of First Name as Test 1 /

}

/ <strong>Main Method</strong>*/

public static void main(String[] args) {

xpathLoc(2); --- This step calls the xpathLoc() method to identify the first name.

}

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

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

Заключение

В этой статье мы рассмотрели, как можно использовать функции XPath contains(), starts-with() и text() с атрибутом и текстом для однозначной идентификации элементов в структуре HTML DOM.

Ниже приведены некоторые замечания касательно функций XPath:

  1. Используйте метод contains() в XPath, если известна часть постоянно видимого текста или атрибута.

  2. Используйте метод starts-with() в XPath, если известна первая часть постоянно видимого текста или атрибута.

  3. Вы также можете использовать методы contains() и starts-with() со всем текстом или полным атрибутом.

  4. Используйте метод text() в XPath, если вам известен весь видимый текст.

  5. Нельзя использовать метод text() с частичным текстом.

  6. Нельзя использовать метод starts-with(), если начальный текст не используется в XPath или если начальный текст постоянно изменяется.

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


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

Об этом расскажем на открытом вебинаре. Регистрируйтесь

Узнать подробнее о курсе "Java QA Automation Engineer" можно
здесь.

ЗАБРАТЬ СКИДКУ

Подробнее..

Обход Incapsula c помощью selenium

21.01.2021 20:07:17 | Автор: admin

Обратился заказчик, с проблемой, что его сборщик не можетсправитьсяс защитой "incapsula".
В двух словах, вместо кода страницы возвращается javascript код, при выполнении которого, идет запрос на сервера инкапсулы, проверяютсякакие-топараметры браузера и в случае если браузер признан валидным, отдается страница и некоторые cookie.

Подробное описание есть на сайте разработчика (www.imperva.com)

Добавление обработчика javascript, так же как и другие решения, предлагаемые гуглом(например поднять свои сервера), показалось слишком сложными\долгими. Selenium же, как оказалось, прекрасно обходит, данную защиту, но так как данных много и собирать в один поток, (или даже переключаясь между вкладками) не хотелось, а запускать несколько браузеров не хватало ресурсов было решено написать proxy сервер.

Так как нагрузка менялась, в зависимости от времени суток и других условий, было решено сделать масштабируемую веб часть через комбинацию Nginx + uwsgi + flask. Запускать на каждый, воркер,версию Seleniumпоказалосьслишком затратным, поэтому вынести Selenium было решено в отдельный сервис, со связью между блоками через Redis. Чтобы максимальноупроститьреализацию, запросы выполняются синхронно.

Структура проекта


uwsgi.ini файл с конфигурацией. Существует куча статей по настройке, поэтому я опущу этот этап. На всякий случай в конце статьи оставлю ссылку по настройке(

Микросервисselenium:
Файл gecko/Sel.py

Класс с sellenium модулем. Реализована инициализация, запуск selenium безгуи, и авторизация на сайте (проект изначально писался под конкретный сайт). Также в цикле обновляется главная страница для получения обновленных cookie и в случае появления сообщения в очереди Redis. Cookie выгружаются и хранятся в общем словаре, в redis. Получение cookie отселениумавынесены в callback функции.

Микросервис API:

Файлы в папке src
Во фласке реализован только базовый функционал, слушающий 1 url:

@app.route('/', methods=['GET', 'POST'])

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

Например:

http://127.0.0.1:5000/?url=https://www.example.com/vehicledetails/34313441?RowNumber=0& 

Запрос без изменений и модификаций передается дальше, что впрочем, не мешает редактировать его, в случае необходимости.

В файле request.py реализована следующая логика.
Инициализацияпараметров для библиотеки requests,такие какхедеры.
Инициализация подключения к Redis, а также Post, Get запросы c помощью библиотеки reqests.
При получении сообщения, происходит обновление cookie, полученных от Selenium сервиса.

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

Весь проект доступен по ссылке

Настройка uwsgi сервера

Подробнее..

Перевод Я научился кодить в основном благодаря порно

07.05.2021 12:20:51 | Автор: admin
image

и оно научило меня довольно полезным навыкам.

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

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

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

Затем при помощи инструмента imacros, который вам, вероятно, знаком, я открыл целый мир скриптинга. Этот инструмент имеет функцию записи макросов, позволяющую автоматизировать действия без единой строки кода (но при желании его можно писать).

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

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

Не стоит и говорить, что у меня развилась зависимость и мне хотелось большего. Моя жизнь начала катиться под откос и достигла нового дна, когда я создал полностью автоматизированный процесс из приложения на C#, который переключался между пользователями, выполнял вход в профили и автоматизировал блоги (tumblr имел ограничение очереди в 300 постов).

Приложение работало на невероятно костыльной основе консольное приложение генерировало блоки кода iMacros, сохраняло их в папку, а затем использовало ассемблерные вызовы с параметрами для запуска браузера Chrome со скриптом iMacros. Славное было время.

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

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

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

Также это научило меня тому, что необходимо правильно документировать код, я добирался до пятой страницы поиска Google так часто, как никогда раньше. Благодаря этому мне удалось автоматизировать Queue tumblr и добавить разные параметры, заголовки и ссылки на мой веб-сайт. Я узнал о Spintax и использовал магию регулярных выражений, чтобы обеспечить создание уникальных заголовков со случайными интервалами. Это захватывало.

Закончив с этим, я начал изучать Selenium, который бесконечно мощнее, чем iMacros, и может выполняться из кода на Python. Итак, теперь я начал разработку качественного решения, использовать систему управления версиями, встроенные сообщения мониторинга и обработку ошибок на случай, если что-то перестанет работать.

Меня обуяла жадность, я решил погрузиться в изучение разработки собственных нишевых веб-сайтов, встраивающих видео с крупных сайтов. Любопытно, что эти сайты не просто позволяют делать подобное, но и платят за любой трафик, который на них приводит. Так я узнал о статических веб-сайтах, использующих довольно продуманную Jekyll-систему с файлами постов, генерируемыми Python. В них были встроенные даты, позволяющие публиковать по десять постов в день из списка тысяч видео; я использовал веб-сайт с триггером cron, вызывающий раз в час обработчик сборок с Netlify. Если новый пост был готов, он сразу публиковался.

И как-то всё это работало. Я написал множество строк кода, провёл кучу исследований, совершил море проб и ошибок. Я перестал играть в видеоигры, потому что это было интереснее. Потом я добрался до крипты благодаря одному парню, рассказавшему об одной рекламной сети с оплатой в биткойнах, иногда выплачивавшей за день огромные суммы в зависимости от обменного курса. Блог на Tumblr, который раньше зарабатывал 2 доллара в день, теперь часто приносил по 20.

А потом Tumblr решил совершить судоку и забанить весь взрослый контент, потеряв в результате примерно 30% своего общего трафика. Позже его купили за смехотворно низкую сумму. Вся моя система рухнула всего за месяц, к счастью, я не успел ещё уволиться с моей основной работы.

Но объём знаний, полученных мной в процессе этой работы, огромен, от автоматизации к созданию веб-сайта, а затем к Python, и всё это сегодня работает на меня. Ничему этому я бы не научился на моей обычной работе, и я рад, что начал путешествие вниз по этой кроличьей норе.

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


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

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

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



На правах рекламы


Подыскиваете VDS для отладки проектов, сервер для разработки и размещения? Вы точно наш клиент :) Посуточная тарификация серверов, создавайте собственную конфигурацию в несколько кликов, антиDDoS уже включен в стоимость.

Подписывайтесь на наш чат в Telegram.

Подробнее..

Перевод Как байпасить reCaptcha V3 с помощью Selenium Python?

10.06.2021 16:07:13 | Автор: admin

*bypass - обход

Мы будем использовать библиотеку python Selenium для байпаса google reCaptcha v3. Следуйте пошаговой инструкции, чтобы получить результат.

Для примера мы будем использовать демо-версию Google reCaptcha api.

Здесь ссылка: https://www.google.com/recaptcha/api2/demo

Сначала необходимо отключить настройку защиты контента в браузере Chrome.

Для этого зайдите в Настройки в Chrome. И напишите "настройки сайта" в строке поиска.

Перейдите в настройки сайта и найдите "Защищенный контент".

Перейдите к защищенному контенту и отключите его.

Теперь перейдем к части кодирования.

В этой статье мы будем работать с Python 3. Мы будем использовать две библиотеки. Если вы хотите настроить Selenium и узнать, как это сделать - изучите эту статью: https://medium.com/@mrabdulbasit1999/selenium-with-python-web-automation-f85dfa2e58fa

Двигаемся дальше,

Установите библиотеку Beautiful Soup для скрипта.

pip install beautifulsoup4

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

from selenium import webdriverfrom selenium.webdriver.common.keys import Keysfrom webdriver_manager.chrome import ChromeDriverManagerfrom selenium.webdriver.common.by import Byfrom http_request_randomizer.requests.proxy.requestProxy import RequestProxyimport os, sysimport time,requestsfrom bs4 import BeautifulSoup

Установите "delayTime" и "audioToTextDelay" в соответствии с вашей скоростью интернета. Установленные значения работают для всех.

delayTime = 2audioToTextDelay = 10

byPassUrl - это URL, на который вам нужно ориентироваться. Опция используется для выбора драйвера chrome, и ей передаются некоторые аргументы.

filename = 1.mp3byPassUrl = https://www.google.com/recaptcha/api2/demo'googleIBMLink = https://speech-to-text-demo.ng.bluemix.net/'option = webdriver.ChromeOptions()option.add_argument('--disable-notifications')option.add_argument("--mute-audio")

Остальная часть кода приведена ниже. Теперь я объясню, как это работает.

Когда скрипт запускается, проверяется поле I'm not a robot.

И дальше все появляется (как обычно).

После по скрипту выбирается кнопка аудио внизу слева.

И появляется вот это. После этого загружается аудио с именем "1.mp3".

Это займет несколько секунд, не волнуйтесь. После этого в браузере откроется новая вкладка, которая перейдет от речи watson к конвертеру в текст и загрузит файл.

Как видите, аудиофайл преобразуется в текст. Он копирует текст и вставляет его в текстовое поле.

И далее нажимается кнопка "Проверить".

Вот, смотрите... Проблема решена. Если у вас есть какие-либо проблемы и вопросы, пишите. Я отвечу на них как только смогу.

Код


Всех читателей нашего блога приглашаем ознакомиться с курсами по тестированию от OTUS.

- Demo Day курса "Python QA Engineer"

- Demo Day курса "Java QA Automation Engineer".

Подробнее..

Визуализация данных по акциям дивидендных аристократов США в формате веб-приложения

12.12.2020 20:16:02 | Автор: admin

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


Пример смотрите по ссылке https://www.emarkoff.com/dividend_aristocrats_sp500/




Постановка задачи


Представим себе, что перед Вами поставлена задача придумать некий веб-сервис, который будет помогать аналитику или менеджеру принимать решения. Это означает, что Вам потребуется доступ к данным, придется трансформировать данные в удобный для анализа вид и отображать в браузере. А теперь самый важный нюанс! Вы делаете всего-лишь прототип, и у Вас под рукой нет ни свободного дата-инженера, ни веб-программиста, ни математика знакомого с финансовой проблематикой. Это означает, что решения должны быть простыми и быстрыми. Цель такой работы обкатать идею.


Пример, на котором я рассмотрю круг возникающих вопросов, это онлайн-визуализация для выбора акций дивидендных аристократов США. Представим, что предполагаемый пользователь это начинающий частный инвестор (быть инвестором стало очень модным в последнее время). Зачем начинающему инвестору вдруг понадобились дивидендные аристократы? Предположим, что инвестор хочет получить определенный входящий денежный поток. Один из вариантов, когда может понадобиться входящий денежный поток это оплата комиссии за бездействие у брокера (такая комиссия есть, например, у Interactive Brokers).


Первый вопрос, на который надо ответить, приступая к созданию прототипа: А что именно нужно конечному пользователю?. Обратим внимание на то, что наш пользователь не опытный спекулянт, а начинающий инвестор. Следовательно ему не имеет смысла подсовывать торговые терминалы и не стоит его погружать в миллион финансовых показателей. Отталкиваться лучше всего от конкретной задачи: какие выбрать инструменты для получения гарантированного входящего денежного потока. Конечно здесь не один вариант. Из самого простого на ум приходят облигации, дивидендные акции, индексные фонды и т.п.


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


Поиск решения


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


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


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


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


Плавно переходим к скринерам акций. А это как раз вышеупомянутые ресурсы с десятками графиков, тысячами цифр, а также миллионом вкладок и всевозможных настроек. И, что интересно, в независимости от цели инвестирования, отрасли и категории компании скринер показывает один и тот же порой впечатляющий набор показателей. На помощь к нам приходит множество образовательных блогов на Youtube, в социальных сетях и из просторов интернета. А почему бы не совместить два в одном? Знание + финансовые показатели и мультипликаторы = осознанный выбор.


Возьмем конкретную цель инвестирования и заранее выбранную категорию компаний. Наша цель обеспечить себе гарантированный входящий поток для оплаты комиссий брокера за бездействие (отсутствие операций). Категория компаний дивидендные аристократы входящие в индекс S&P 500.


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


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


Самый простой способ инвестировать в дивидендных аристократов это купить ETF на индекс S&P 500 Dividend Aristocrats Index. Но, если мы играем в игру обгони индекс, нам надо уметь отбирать из компаний дивидендных аристократов только наиболее доходные (с учетом текущей цены акций) и надежные компании. Вспомним, что мы говорим о начинающем инвесторе. Как же можно агрегировать информацию о надежности и доходности дивидендных акций на одном графике, чтобы помочь отобрать компании для инвестирования?


Найдено следующее решение. Показать на одном графике доходность и надежность акций. За показатель доходности берем форвардную годовую дивидендную доходность (forward annual dividend yield ), то есть тот годовой процент, на который инвестор может рассчитывать в будущем с учетом ежегодного роста дивидендов.


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


Обратимся за помощью к рейтинговым агентствам. Оказывается, что надежность компаний интересует не только нас как инвесторов, но и Банки и другие кредитные организации, которые дают компаниям деньги в долг или покупают облигации. В таких случаях часто используют данные о кредитных рейтингах компаний. Детальный обзор видов рейтингов и рейтинговых агентств в рамки данного мини-проекта не входил. В качестве примера были взяты долгосрочные кредитные рейтинги в иностранной валюте всем известного рейтингового агентства Standard & Poors.


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


В итоге в качестве способа визуализации был выбран scatter plot, на котором все компании входящие в группу дивидендных аристократов отображаются в виде маркеров, а по осям отложены значения Forward Annual Dividend Yield и Payout Ratio. График интерактивный и во всплывающей подсказке при наведении курсора на соответствующий маркер дает информацию о компании и ее рейтинге.


Создание веб-сервиса


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


Первое с чего следует начинать реализовывать техническую часть проекта это выбор хостинга. В качестве хостинг-провайдера был выбран pythonanywhere.com , и сделано это было главным образом из-за того, что на pythonanywhere.com изначально припаркован весь мой сайт. Останавливаться подробно на этом пункте не буду. Краткое описание можно посмотреть здесь.


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


Если кому интересно, могу посоветовать зайти на маркетплейс веб-API https://rapidapi.com/ На маркетплейсе представлен широкий спектр разнообразных API разбитый на категории news, finance, medical, search и т.д. Всего представлено более 40 категорий. Но большинство API условно бесплатные или полностью платные. Условно бесплатные API отличаются тем, что для свободного использования установлена месячная квота по общему числу запросов и ограничено количество запросов в единицу времени.


Второй вариант доступа к данным заключается в использовании специализированных библиотек. Back-end сервиса писался на python, поэтому в данном случае было полезно зайти в репозиторий пакетов https://pypi.org/ . На pypi.org имеется удобный фильтр по классификаторам Filter by classifier. Далее выбираете тему Financial или Investment. К примеру в репозитории можно найти библиотеку для доступа к данным Yahoo Finance под названием yfinance или библиотеку finvizlite 0.1.1. для доступа к скринеру акций finviz.com .


Третий вариант получить данные свободно публикуемые в интернете сводится к самостоятельному веб-скрейпингу. И, когда говорят о веб-скрейпинге на python, часто имеют в виду библиотеки BeautifulSoup и Selenium. Было выполнено небольшое тестирование этих библиотек, при котором вебскрейпинг запускался по расписанию с помощью встроенного сервиса pythonanywhere.


На этом пункте немного остановлюсь, поскольку есть очень важный момент, связанный с использованием билиотеки Selenium. В отличие от BeautifulSoup, когда в ответ на запрос к серверу загружается и парсится html-страница, при вызове Selenium должен быть запущен практически полноценный браузер. Такой веб-скрейпинг назвается динамическим, так как внутри браузера выполняется код JavaScript, динамически загружаются все данные, а доступ к элементам страницы осуществляется через объектную модель документа ( Document Object Model). Поэтому очень важно при выборе хостинга поинтересоваться, предусмотрена ли возможность запуска браузера из кода на python по умолчанию. Это сэкономит Вам часть времени и сил. Например, на pythonanywhere.com предусмоторен запуск виртуального дисплея Xvfb и поддержка веб-браузера Firefox.


И наконец, расскажу как же все-таки была организована работа с данными в рамках настоящего сервиса. Данные о рейтингах были однократно извлечены методом вебскрейпинга с сайта Standard & Poors.. Реализовывать автоматизированный запуск заданий с обновлением информации о списке дивидендных артистократов и их рейтингах не стал. Любое изменение html-разметки может привести к некорректному сбору данных. Следовательно, автоматизация все равно будет не полной и потребует технической поддержки в будущем.


Показатели Forward Annual Dividend Yield и Payout Ratio собираются с Yahoo Finance с помощью библиотеки yfinance. Значения этих показателей хранятся в виде обычного файла csv и автоматизированно обновляются один раз в час.


И напоследок хотел бы отметить еще один важный нюанс. Библиотека yfinance не является официальным API к Yahoo Finance. Следовательно, в любой момент на Yahoo Finance могут произойти изменения в html-разметке, формате и составе данных или собственном API, если таковое используется автором библиотеки yfinance, и никто никого предупреждать специально об этом не будет. А вы получите в лучшем случае ошибку, а в худшем неадекватные данные. Автору же бесплатной библиотеки не позвонишь и не потребуешь у него срочно залатать неожиданно образовавшийся баг.

Подробнее..

Scala Selenium. Сколько человек в сборной имеют более одного гражданства?

27.03.2021 12:17:15 | Автор: admin

Рассмотрим пример использования Selenium на Scala, отвечая на вопрос "Сколько человек в каждой футбольной сборной имеют более одного гражданства?"

За основу возьмем данные с сайта transfermarkt.com:

List of football/soccer teams

Переход на заданную страницу, если она реализует trait org.scalatestplus.selenium.Page, может осуществляться так:

import org.scalatestplus.selenium.Pageimport org.scalatestplus.selenium.WebBrowser._import org.openqa.selenium.WebDriverimplicit def webDriver: WebDriver = ??? /* from container */class RankingListPage(implicit val webDriver: WebDriver) extends Page {   val url = "https://www.transfermarkt.com/statistik/weltrangliste/statistik"}val rankingListPage = new RankingListPage()go to rankingListPage

После перехода прежде чем работать со страницей необходимо дождаться окончания отрисовки её элементов. Будем ориентироваться на кнопку Compact и дождемся, когда она станет видима.

Xpath локатор кнопки будет таким:

import org.scalatestplus.selenium.WebBrowser._val compactTab: Query = xpath("//div[.='Compact']")

Ожидание видимости элемента осуществляется так (timeout можно задать в конфиге, query - заданный элемент):

import org.openqa.selenium._import org.openqa.selenium.support.ui.WebDriverWaitimport org.openqa.selenium.support.ui.ExpectedConditionsimport org.scalatestplus.selenium.WebBrowser._import java.time.Durationdef waitVisible(query: Query, timeout: Int)(implicit webDriver: WebDriver): WebElement =    new WebDriverWait(webDriver, Duration.ofSeconds(timeout)).until(ExpectedConditions.visibilityOfElementLocated(query.by))

Рассмотрим переход на закладку Compact.

Переход на закладку будет состоять из следующих шагов:

  • Проверяем, активна ли закладка (активная закладка в данном случае в атрибуте class содержит "active").

  • Если да, то ничего не делаем переход осуществлен.

  • Если нет, то кликаем на закладку и ждём, когда закладка станет активна.

Проверить, что элемент query: Query содержит заданное значение в атрибуте class можно так:

def doesClassContain(value: String): Boolean =    (for {      element   <- find(query)      attribute <- element.attribute("class")    } yield attribute.contains(value)).contains(true)

Кликнуть на элемент можно так: clickOn(query)

Ожидание, когда атрибут class элемента будет содержать заданное значение, можно реализовать так:

def waitClassContain(value: String): Boolean =  new WebDriverWait(driver, Duration.ofSeconds(timeout)).until(ExpectedConditions.attributeContains(query.by, "class", value))

Итого:

def clickCompact(): Unit =    if (!compactTab.doesClassContain("active")) {      clickOn(compactTab)      val _ = compactTab.waitClassContain("active")    }

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

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

Логика будет такой:

  • Проверяем, достигли ли последней страницы

  • Если нет, считываем список со страницы, переходим на следующую и возвращаемся на пункт выше

  • Если дошли до последней страницы, то считываем список с неё

Для того, чтобы проверить, достигли ли мы последней страницы, достаточно проверить, есть ли кнопка перехода на следующую страницу (см. скрин выше, css локатор li.naechste-seite > a):

val nextPageLink: Query = cssSelector("li.naechste-seite > a")def isPresent: Boolean = find(nextPageLink).isDefined

Для того, чтобы считать список стран, необходимо найти все элементы с xpath локатором //table/tbody//a[count(*)=0] и у каждого элемента считать text и значение атрибута href (или //table/tbody/tr[td[.='CONMEBOL']]//a[count(*)=0] - если интересна только одна конфедерация, например, самая маленькая - CONMEBOL(Южная Америка)):

val itemLink: Query = xpath("//table/tbody//a[count(*)=0]")def items(): Seq[(String, Option[String])] =  findAll(itemLink).map(el => (el.text.trim, el.attribute("href"))).toSeq

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

Чтобы удостовериться, что мы перешли на следующую страницу, мы можем считать номер текущей страницы (css локатор li.selected > a), а после клика на nextPageLink дождаться, когда номер текущей страницы станет на 1 больше:

val selectedPageLink: Query = cssSelector("li.selected > a")def clickNextPage(): Unit = {  val nextPage = find(selectedPageLink).map(_.text).get().toInt + 1  clickOn(nextPageLink)  val _ = webDriverWait(driver).until(ExpectedConditions.textToBe(selectedPageLink.by, nextPage.toString))}

Соединяем все воедино и получаем следующий список (на 13 марта 2021):

Теперь, когда у нас есть url страны можно составить список игроков сборной.

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

Ссылка на страницу игрока - это xpath локатор //table/tbody//span[@class='hide-for-small']/a[count(*)=0]:

val itemLink: Query = xpath("//table/tbody//span[@class='hide-for-small']/a[count(*)=0]")

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

Для начала нужно перейти на закладку Profile.

Мы уже переходили на закладку на предыдущих страницах. Здесь будет то же самое, за исключением одного нюанса: если раньше у активной закладки менялся атрибут class,то теперь этот атрибут меняется не у самой ссылки, а у её родителя. Кстати, в этот раз немецкие разработчики сайта значение атрибута class активного элемента не стали переводить на английский, а оставили на немецком - "aktiv":

Создадим два элемента: ссылку и её родителя, а затем определим переход на закладку так:

val profileTab: Query  = xpath("//li[@id='profile']")val profileLink: Query = xpath("//li[@id='profile']/a")def clickProfile(): Unit =   if (!profileTab.doesClassContain("aktiv")) {    clickOn(profileLink)    val _ = profileTab.waitClassContain("aktiv")  }

Теперь осталось только определить гражданство. Для этого возьмем элемент img из строки Citizenship: и считаем её атрибут title:

val citizenshipImg: Query = xpath("//th[.='Citizenship:']/following-sibling::td/img")def citizenship(): Seq[String] = findAll(citizenshipImg).flatMap(_.attribute("title")).toSeq

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

Results (for Russia, Ukraine and Belarus)

Country name

%

Foreigners

Russia

11% (3/28)

(Brazil (1) -> (Mrio Fernandes), Kyrgyzstan (1) -> (Ilzat Akhmetov), Germany (1) -> (Roman Neustdter))

Ukraine

9% (3/33)

(Brazil (2) -> (Marlos, Jnior Moraes), Hungary (1) -> (Igor Kharatin))

Belarus

4% (1/25)

(Cameroon (1) -> (Maks Ebong))

В наших сборных только 3 натурализованных игрока (и все из Бразилии). Остальные родились в СССР.

Results (for CONMEBOL)

Country name

%

Foreigners

Brazil

36% (9/25)

(Spain (3) -> (Casemiro, Bruno Guimares, Vincius Jnior), Italy (1) -> (Alex Telles), France (1) -> (Thiago Silva), Portugal (4) -> (Ederson, Marquinhos, Allan, Lucas Paquet))

Argentina

57% (13/23)

(Spain (2) -> (Gonzalo Montiel, Lionel Messi), Italy (11) -> (Lucas Martnez Quarta, Wlter Kannemann, Nicols Tagliafico, Guido Rodrguez, Rodrigo de Paul, Giovani Lo Celso, Nicols Domnguez, ngel Di Mara, Joaqun Correa, Papu Gmez, Lucas Alario))

Uruguay

51,5% (18/35)

(Spain (7) -> (Jos Mara Gimnez, Sebastin Coates, Diego Godn, Agustn Oliveros, Damin Surez, Lucas Torreira, Federico Valverde), Paraguay (1) -> (Rodrigo Muoz), Italy (10) -> (Fernando Muslera, Martn Campaa, Sergio Rochet, Matas Via, Franco Pizzichillo, Nahitan Nndez, Matas Vecino, Giorgian de Arrascaeta, Diego Rossi, Cristhian Stuani))

Colombia

22% (6/27)

(Spain (4) -> (Jeison Murillo, Johan Mojica, James Rodrguez, Luis Surez), Argentina (1) -> (Frank Fabra), England (1) -> (Steven Alzate))

Chile

21% (5/24)

(Haiti (1) -> (Jean Beausejour), Spain (3) -> (Claudio Bravo, Gary Medel, Fabin Orellana), Italy (1) -> (Luis Jimnez))

Peru

33% (12/36)

(Venezuela (1) -> (Carlos Ascues), Spain (3) -> (Alexander Callens, Cristian Benavente, Sergio Pea), Uruguay (1) -> (Gabriel Costa), Italy (2) -> (Luis Abram, Gianluca Lapadula), Netherlands (1) -> (Renato Tapia), Switzerland (1) -> (Jean-Pierre Rhyner), Portugal (1) -> (Andr Carrillo), Croatia (1) -> (Ral Ruidaz), Lebanon (1) -> (Matas Succar))

Venezuela

25% (7/28)

(Spain (4) -> (Roberto Rosales, Juanpi Aor, Darwin Machs, Fernando Aristeguieta), Switzerland (1) -> (Rolf Feltscher), England (1) -> (Luis Del Pino Mago), Colombia (1) -> (Jan Hurtado))

Paraguay

21% (7/33)

(Spain (1) -> (Antonio Sanabria), Argentina (4) -> (Santiago Arzamendia, Gastn Gimnez, Andrs Cubas, Ral Bobadilla), Italy (2) -> (Antony Silva, Ivn Piris))

Ecuador

12% (4/33)

(Spain (3) -> (Erick Ferigra, Pervis Estupin, Leonardo Campana), Argentina (1) -> (Hernn Galndez))

Bolivia

25% (7/28)

(United States (2) -> (Adrin Jusino, Antonio Bustamante), Spain (1) -> (Jaume Cullar), Argentina (1) -> (Carlos Lampe), Brazil (1) -> (Marcelo Moreno), Switzerland (1) -> (Boris Cespedes), Portugal (1) -> (Erwin Snchez))

А вот в Южной Америке людей с двойным гражданством довольно много. Впрочем, это неудивительно: в чемпионатах Евросоюза жесткий лимит на легионеров (в заявке только 3 игрока с гражданством не ЕС), поэтому южноамериканцам, чтобы попасть в Европу, приходится либо пытаться получить гражданство бывшей митрополии (Бразилия -> Португалия, остальные -> Испания), либо искать среди своих предков итальянцев. Второе не так сложно, как кажется. Во время Второй Мировой войны Южная Америка хоть и была на бумаге нейтральной, по факту разделилась на два лагеря: Бразилия -> союзники, Аргентина + Уругвай -> фашисты. Поэтому неудивительно, что после 1945 года многие итальянцы в поисках лучшей жизни иммигрировали из разрушенной фашисткой Италии в симпатизировавшим ей Аргентине и Уругваю. Поэтому современному аргентинцу или уругвайцу получить гражданство Италии не сложнее, чем человеку по фамилии Зильберман - гражданство Израиля - кто-нибудь среди предков нужной национальности да найдётся!

Source code

Подробнее..

Scala Selenium. Самый стремительный взлет в Лиги наций УЕФА?

01.04.2021 20:04:02 | Автор: admin

Какой самый стремительный взлет в Лиги наций УЕФА?

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

С момента запуска Лиги наций УЕФА прошло целых два розыгрыша и уже можно подвести промежуточные результаты:

  • России два раза подряд занимала второе место в группе дивизиона B (оба раза блестяще начиная и столь же блестяще-позорно заканчивая) и в третьем розыгрыше предсказуемо выступит в том же дивизионе B - вот это стабильность (со слезами на глазах)

  • Сборная Турции два раза подряд занимала последнее место в своей группе (оба раза в той же самой, где была и сборная России), но в третьем розыгрыше выступит всего лишь одним дивизионом ниже - в дивизионе С

  • А вот и обещанный спойлер: сборная Венгрии в первом розыгрыше заняла второе место в третьем по силе дивизионе С, но в третьем розыгрыше выступит в самом сильном дивизионе А вместе с топ-сборными - вот это феерический взлет. Жаль что он произошел в той же самой группе, где была и сборная России.

А как же выступили остальные сборные? После того, как интрига была напрочь уничтожена, можно заняться автоматизацией. За основу возьмем данные с сайта transfermarkt.com: результаты сборных в Лиге наций

Определимся с понятиями

В лиге наций УЕФА 4 по силе дивизиона: A (самый сильный), B, C, D. Победитель дивизиона поднимается в более сильный дивизион, занявший последнее место - опускается дивизионом ниже. В первом розыгрыше в сильных дивизионах было только по 3 команды, но после этого УЕФА решила, что топ-матчей должно быть больше и во втором розыгрыше во всех дивизионах, кроме самого слабого (D), стало выступать по 4 команды. Именно по причине необходимой доукомплектации более сильных дивизионов после первого розыгрыша Турция, занявшая последнее место, не вылетела, а Венгрия, занявшая второе - поднялась выше.

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

Страница дивизиона Лиги Наций УЕФА

Для того, чтобы перейти на страницу группы X необходимо выполнить следующее:

  • Перейти на главную страницу сайта transfermarkt.com

  • В верхнем меню нажать на пункт Competitions

  • Во всплывающем меню выбрать пункт UEFA Nations League X

Xpath локаторы для ссылок меню Competitions и UEFA Nations League X будут следующие:

val competitionsLink: Query         = xpath("//a[normalize-space(.)='Competitions']")def groupLink(group: String): Query = xpath(s"//a[normalize-space(.)='UEFA Nations League $group']")

Мы вынуждены использовать функцию normalize-space, потому что ссылка меню Competitions содержит не только слово Competitions, но ещё и кучу пробелов с одним переносом строки:

Именно поэтому в локаторе //a[normalize-space(.)='Competitions'] мы ищем элемент с тэгом a, текстовое содержимое которого после нормализации пробелов равно Competitions.

Ожидание появления элемента query можно реализовать так:

new WebDriverWait(driver, Duration.ofSeconds(timeout)).until(ExpectedConditions.visibilityOfElementLocated(query.by))

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

competitionsLink.waitVisible().click()groupLink(group).waitVisible().click()

Конечно, после перехода на страницу группы Лиги Наций нужно подождать, когда эта страница загрузится. Для этого возьмем элемент заглавия страницы с css-локатором div.dataName > h1:

и подождем, когда в нём отобразится название группы:

val header: Query = cssSelector("div.dataName > h1")new WebDriverWait(driver, Duration.ofSeconds(timeout)).until(ExpectedConditions.textToBe(header.by, s"UEFA Nations League $group"))

Результаты в дивизионе

В правом нижнем углу страницы дивизиона есть результаты выступлений сборных:

Строка с результатами сборной будет иметь xpath-локатор //table//tr[td[@class='rechts']]. Всего у нас будет до 16 строк и каждую строку нужно обработать - выудить из строки место сборной (оно необходимо, чтобы определить состав дивизионов в третьем розыгрыше) и, конечно, название страны. К сожалению, Scala библиотека для Selenium довольно бедная и не позволяет искать подэлементы относительно заданного, поэтому воспользуемся Java-библиотекой:

val resultRow: Query = xpath("//table//tr[td[@class='rechts']]")import scala.jdk.CollectionConverters._import org.openqa.selenium._val webDriver: WebDriver = ???def results: mutable.Buffer[(Int, String)] =  webDriver.findElements(resultRow.by).asScala.map { el =>    {      val place = el.findElement(By.xpath(".//td[@class='rechts']")).getText      val name  = el.findElement(By.xpath(".//td[contains(@class, 'hauptlink')]")).getText      (place.toInt, name)    }  }

Здесь происходит следующее:

  • находим все элементы с xpath-локатором //table//tr[td[@class='rechts']]

  • конвертируем java.util.List в scala.collection.mutable.Buffer

  • для каждого элемента из первого пункта находим два его дочерних подэлемента: .//td[@class='rechts'] - место в таблице, .//td[contains(@class, 'hauptlink')] - название страны

  • считываем текст из дочерних элементов

Получим следующую картину, например, для дивизиона D:

Place

Country

1

Faroe Islands

2

Malta

3

Latvia

4

Andorra

1

Gibraltar

2

Liechtenstein

3

San Marino

Previous results

Для того, чтобы получить результаты предыдущего сезона, необходимо в фильтре Filter by season: выбрать значение 18/19 и нажать на кнопку Show:

Здесь будет больше действий и проверок:

  • В первую очередь нужно раскрыть список доступных сезонов (нажать на кнопку с css-локатором a.chzn-single > div > b)

  • Затем кликнуть на предыдущий сезон (нажать на ссылку с xpath-локатором //li[.='18/19'])

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

  • Затем необходимо нажать на кнопку Show (css-локатор input[value='Show'])

  • И подождать, когда произойдет переход на страницу предыдущего сезона (для этого будем ждать, когда url текущей страницы станет оканчиваться на saison_id=2018 (url на немецком))

val selectSeason: Query   = cssSelector("a.chzn-single > div > b")val previousSeason: Query = xpath("//li[.='18/19']")val show: Query           = cssSelector("input[value='Show']")def selectPreviousSeason: Boolean = {  selectSeason.waitVisible().click()  previousSeason.waitVisible().click()  new WebDriverWait(driver, Duration.ofSeconds(timeout)).until(invisibilityOfElementLocated(previousSeason.by))  clickOn(show)  new WebDriverWait(driver, Duration.ofSeconds(timeout)).until(wd => wd.getCurrentUrl.endsWith("saison_id=2018"))}

Теперь можно собрать все воедино и получим следующее:

  • Переходим на главную страницу сайта

  • Для каждой из групп 'A', 'B', 'C', 'D' выполняем:

  • Переходим в заданную группу

  • Считываем результаты второго розыгрыша

  • Сохраняем их

  • Переходим в предыдущий сезон

  • Считываем результаты первого розыгрыша

  • Сохраняем их

P.S. Забегая вперед, скажу, что за это время Macedonia сменила имя на North Macedonia - это пришлось учесть.

case class Result(number: Int, group: Char, place: Int, country: String)val mainPage = new MainPagego to mainPageval results: ArrayBuffer[Result] = ArrayBuffer.empty[Result]Seq('A', 'B', 'C', 'D').foreach(group => {  val leagueGroupPage = mainPage.goToGroup(group.toString)  val groupResult     = leagueGroupPage.results  groupResult.foreach { case (place, country) => results += Result(2, group, place, country) }  leagueGroupPage.selectPreviousSeason  leagueGroupPage.waitLoad(group.toString)  val previousSeasonResult = leagueGroupPage.results  previousSeasonResult.foreach {    case (place, country) =>      results += Result(1, group, place, country.replace("Macedonia", "North Macedonia"))  }})

Теперь осталось только обработать результаты.

Результаты у нас в виде коллекции case class Result(number: Int, group: Char, place: Int, country: String), приведем эту коллекцию к коллекции case class ParsedResult(country: String, firstSeason: (Char, Int), secondSeason: (Char, Int), thirdSeason: Char, progress: (Int, Int)), где сезон представлен в виде tuple (дивизион, итоговое место), а прогресс - из двух цифр, обозначающих прогресс по итогам розыгрыша: 1 (повышение в классе) | 0 | -1 (понижение)

import scala.collection.mutable.ArrayBuffercase class Result(number: Int = 0, group: Char = 'E', place: Int = 0, country: String = "")case class ParsedResult(country: String,                        firstSeason: (Char, Int),                        secondSeason: (Char, Int),                        thirdSeason: Char,                        progress: (Int, Int))val results: ArrayBuffer[Result] = ???val parsedResults = results        .groupBy(_.country)        .view        .mapValues(seq => {          val country: String           = seq.head.country          val firstRes                  = seq.find(_.number == 1).getOrElse(Result())          val firstSeason: (Char, Int)  = (firstRes.group, firstRes.place)          val secondRes                 = seq.find(_.number == 2).getOrElse(Result())          val secondSeason: (Char, Int) = (secondRes.group, secondRes.place)          val thirdSeason: Char =            if (secondSeason._2 == 1 && secondSeason._1 != 'A') (secondSeason._1 - 1).toChar            else if (secondSeason._2 == 4 && secondSeason._1 != 'D') (secondSeason._1 + 1).toChar            else secondSeason._1          val progress: (Int, Int) = (firstSeason._1 - secondSeason._1, secondSeason._1 - thirdSeason)          ParsedResult(country, firstSeason, secondSeason, thirdSeason, progress)        })        .values        .groupBy(_.progress)

Самые успешные сборные?

Разделим полученный результат на группы сборных, объединенных по достигнутому прогрессу.

Получим следующий результат:

Суперпрогресс (сборные, совершившие прорыв через два дивизиона) - 2

Country

1st

2nd

3rd

Hungary

C(2)

B(1)

A

Armenia

D(2)

C(1)

B

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

Поднялись и закрепились - 13

Country

1st

2nd

3rd

Denmark

B(1)

A(2)

A

Romania

C(2)

B(3)

B

Scotland

C(1)

B(2)

B

Finland

C(1)

B(2)

B

Israel

C(2)

B(3)

B

Norway

C(1)

B(2)

B

Luxembourg

D(2)

C(2)

C

Azerbaijan

D(2)

C(3)

C

Georgia

D(1)

C(3)

C

Belarus

D(1)

C(2)

C

Kosovo

D(1)

C(3)

C

North Macedonia

D(1)

C(2)

C

и т.д. Полный список результатов тут. А исходный код тут.

Предыдущая статья: Scala + Selenium. Сколько человек в сборной имеют более одного гражданства?

Подробнее..

Callisto. Зачем мы придумали замену Selenium Grid

28.01.2021 16:20:38 | Автор: admin

На Хабре уже не раз писали о том, что у Selenium Grid есть проблемы, которые не решить простым способом (например: раз, два, три). В этой статье мы поделимся нашим опытом и расскажем, как нам в Wrike удалось построить стабильную инфраструктуру для Selenium-тестов.

TLDR: Мы написали своё open source решение и полностью заменили им Selenium Grid.

Мы уже рассказывали о том, как масштабировали свою Selenium-ферму с помощью Google Cloud Engine и Kubernetes. От очередей на запуск тестов мы избавились, но из QA-департамента регулярно поступали жалобы на нестабильность тестовой инфраструктуры.

Выбор пути

У нас не получилось заставить работать Selenium Grid в GKE так, чтобы нас это устраивало. В GKE мы использовали короткоживущие Preemptible VMs. Запуски тестов создают непродолжительную, но интенсивную нагрузку, и именно для таких случаев хорошо подходят эти короткоживущие инстансы. Их использование позволяет снизить расходы на инфраструктуру в несколько раз. Но, так как они могут быть выключены в любой момент, это увеличивает нестабильность Selenium Grid, который и сам по себе достаточно нестабилен. Так мы пришли к выводу, что надо искать ему замену.

Больше всего нам понравился Selenoid от Aerokube, в том числе потому, что в нём не используются компоненты Selenium Grid (Selenium Hub и Selenium Node). Но, к сожалению, он не работает в Kubernetes, а это было критично для нас: мы широко используем его в других частях нашей инфраструктуры.

В качестве альтернатив, работающих в Kubernetes, мы рассматривали Zalеnium, jsonwire-grid и Moon. Zalenium и jsonwire-grid используют в своей архитектуре Selenium Node, а мы очень хотели этого избежать. К тому же Zalenium прекратили разрабатывать.

Moon выглядел неплохо, но это платное решение с закрытым исходным кодом. У него богатая функциональность: например, в конфигурации Moon можно указать список поддерживаемых браузеров, организовав единую точку входа для всех тестов (такой подход используют в Яндексе). Нам это не требовалось, так как мы делали по-другому перед запуском тестов запускали Selenium Grid, настроенный на запуск определенной версии браузера, и после окончания тестов удаляли его.

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

Нужен ли свой велосипед?

Кодовая база Selenium Grid огромна. Но хорошая новость заключается в том, что писать такое же большое и сложное решение не нужно. Много кода в Selenium относится к работе с устаревшими версиями браузеров, а все популярные современные браузеры совместимы со стандартом W3C Webdriver.

В комплекте с каждым браузером идёт сервер, который предоставляет HTTP API (совместимый со стандартом W3C Webdriver) для управления этим браузером (дальше в тексте этот сервер будем называть просто веб-драйвером). Например, для Google Chrome это ChromeDriver, для Mozilla Firefox geckodriver и т. д. Поэтому при написании своего решения не требуется делать полноценную реализацию стандарта W3C она уже реализована в веб-драйвере.

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

С учетом этих условий остается только 2 основные задачи запуск/удаление браузеров и маршрутизация запросов до браузеров. Именно этим и занимается Callisto open source решение, которое мы разработали. Для запуска/удаления браузеров мы написали небольшое приложение, используя готовую библиотеку для работы с Kubernetes, а за маршрутизацию запросов отвечает Nginx. Получилось простое и надежное решение.

Архитектура Callisto

В архитектуре Callisto три основных компонента: Nginx, Callisto и podы с браузером и веб-драйвером.

Nginx делит все запросы на 2 группы:

  1. Запросы на создание/удаление сессии отправляет в Callisto.

  2. Все остальные запросы отправляет напрямую веб-драйверу.

Callisto при получении запроса на создание сессии:

  1. Создает pod с браузером и веб-драйвером.

  2. Перенаправляет запрос на создание сессии веб-драйверу и возвращает ответ.

При получении запроса на удаление сессии:

  1. Возвращает успешный ответ.

  2. Удаляет pod с браузером.

В качестве Docker-образов с браузерами используются образы от Selenoid.

Маршрутизация запросов до браузеров

Важным моментом является маршрутизация запросов от тестов к нужному podу с браузером. Нужно где-то хранить соответствие между session_id, которое вернул веб-драйвер, и конкретным podом. Первое, что приходит в голову это хранение информации на стороне Callisto. Мы немного изучили этот вопрос и смогли найти более простое и изящное решение, благодаря которому Callisto остался stateless-решением.

Как это работает: после того, как веб-драйвер создал сессию, Callisto модифицирует поле session_id в ответе веб-драйвера, добавляя имя и ip-адрес podа, и после этого возвращает ответ тестам. Соответствие session_id pod_ip хранится на стороне тестов.

Благодаря тому, что в стандарте W3C Webdriver session_id используется только в URL-ах, вторую часть обработки достаточно просто выполнить на стороне Nginx. Когда на Nginx приходит запрос, содержащий модифицированный session_id, он извлекает ip-адрес podа и оригинальный session_id и перенаправляет запрос этому podу с оригинальным значением session_id.

Примечание: имя podа Callisto использует при обработке запроса на удаление сессии.

Пример на картинке:

Дополнительные возможности Callisto

Web UI. В качестве Web UI используется Selenoid UI.

В Callisto поддерживается:

  • Отображение текущего статуса Selenium-сессий.

  • VNC.

  • Отображение логов веб-драйвера в реальном времени.

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

Примечание: поддерживаются не все функции Selenoid UI. Например, не работает ручное создание сессий и просмотр видеозаписей.

Мониторинг. Callisto экспортирует метрики в формате Prometheus: можно удобным образом наблюдать за состоянием Selenium-фермы.

Чистое окружение под каждый тест

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

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

Время создания podа с браузером сильно зависит от используемой инфраструктуры, но ориентироваться можно на ~10 секунд (может занимать до 2-х минут, если в Kubernetes-кластере включен автоскейлинг).

С какими проблемами мы столкнулись при разработке Callisto

Кодовая база Callisto небольшая меньше 2000 строк кода. Рассказ о решении был бы неполным без описания проблем, с которыми мы столкнулись при его разработке. На их решение мы потратили больше времени и сил, чем на саму разработку. Надеемся, что для кого-то из читателей эта информация окажется полезной.

499-е ошибки на Nginx. В логах Nginx периодически попадались 499-е ошибки. Они воспроизводились нестабильно: при максимальной нагрузке на Kubernetes-кластер (в середине рабочего дня) их было больше, а утром и вечером они практически не появлялись. При этом на стороне тестов ошибка проявлялась как Server disconnected error.

Nginx считал, что проблема на стороне клиента, а тесты считали, что проблема на стороне сервера.

Ошибка возникала либо на стороне Google Load Balancer, либо на стороне Ingress ControllerОшибка возникала либо на стороне Google Load Balancer, либо на стороне Ingress Controller

В результате исследований мы выяснили, что причина в некорректном значении параметра worker-shutdown-timeout. Его увеличение с 10 секунд (значение по-умолчанию) до 120 секунд помогло решить проблему.

Примечание: в ingress-nginx версии 0.26.0 значение по-умолчанию для этого параметра было увеличено с 10 до 240 секунд.

Медленное создание сервисов. При первоначальной реализации мы создавали Kubernetes-сервис для каждого podа с браузером, при этом имя сервиса соответствовало session_id.

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

При проведении нагрузочных тестов мы столкнулись с проблемой: создание сервисов начинает выполняться очень медленно при росте нагрузки на кластер (мы проверяли на GKE 1.12):

Так как мы хотели запускать 1000+ тестов параллельно в одном кластере, такое положение дел нас не устраивало. Мы пришли к решению с модификацией session_id, которое описано в разделе про маршрутизацию запросов до браузеров.

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

Медленные ответы от Kubernetes-API. При 100 параллельных тестах Kubernetes-API отвечал довольно быстро, но при дальнейшем увеличении нагрузки время ответа составляло уже единицы секунд, что заметно сказывалось на общей производительности. При профилировании мы обнаружили, что количество запросов от Callisto к Kubernetes-API росло экспоненциально с увеличением количества тестов. Мы быстро нашли ошибку в коде и исправили ее.

Медленное создание podов с браузерами. Нам удалось достичь приемлемого времени создания/удаления podов при одновременно бегущих ~500 тестах. Но нам требовалось обслуживать больше 1000 параллельных тестов.

Проблема решилась при обновлении GKE кластера с версии 1.12 до 1.14. В версии 1.14 время создания podов не превышало 10 секунд при более чем 2000 параллельно бегущих тестах.

504-е ошибки на Nginx. Некоторое количество тестов падало из-за того, что Nginx возвращал 504-ую ошибку. Проблема оказалась в том, что мы использовали Preemptible VMs. Когда Google забирал VM, все запросы к podам на этой VM падали по таймауту, что и приводило к данной ошибке.

В итоге мы настроили таймауты на стороне Nginx (чтобы такие запросы не висели долго) и добавили обработку этой ошибки на стороне тестов: при ее возникновении тест перезапускается.

Результаты внедрения Callisto

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

Кроме увеличения стабильности, оказалось, что при примерно том же количестве запускаемых тестов платить за GKE мы стали на 30-40% меньше. Такая экономия достигается за счет того, что Callisto более эффективно использует ресурсы кластера, запуская podы только тогда, когда от тестов приходят запросы. При использовании Selenium Grid pod'ы с браузерами часть времени находятся в запущенном состоянии, ожидая нагрузку. Также каждому pod'у с браузером нужно меньше ресурсов, т.к. не используется Selenium Node.

Красной линией отмечен переход с Selenium Grid на Callisto:

2019-20202019-2020

Репозитории:

Мы каждый день используем Callisto, и его текущая функциональность нас устраивает. В данный момент мы не развиваем его активно, но готовы принимать пул-реквесты.

Будем рады ответить на вопросы и комментарии про наше решение.

Подробнее..

Перевод Как я получил пожизненный запас чесночной пиццы с помощью Python и Selenium

20.10.2020 20:09:53 | Автор: admin

История голодного студента с пытливым умом


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


Papa Johns (с) Заголовок письма с опросом

Бесплатная еда! Мне определённо нужно было пройти этот опрос

Опрос



Papa Johns (с) Завершающая страница опроса

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

Но из любопытства я ещё раз взглянул на ссылку. Похоже, параметр GUID был идентификатором клиента. Угадайте, что произошло, когда я изменил его на что-то случайное? Выскочил совершенно новый опрос с новыми халявными пицца-палочками.

Я мог делать это вечно! Но это не самое эффективное использование моего времени, так что давайте применим немного магии Selenium.

Бот


Selenium Webdriver это фреймворк автоматизации действий в браузере, который в основном используется для тестирования. Я выбрал Python как язык программирования и решил попробовать Selenium для создания опросного бота.

Установка


Для начала запускаем pip install selenium и pip install fake_useragent. Что такое user-agent? Документация MDN определяют его следующим образом:

Заголовок запроса User-Agent это строка, позволяющая серверам и сетевым узлам идентифицировать приложение, операционную систему, поставщика и/или версию агента, который отправил запрос.

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

Кроме того, нужно было скачать ChromeDriver, чтобы взаимодействовать с браузером Chrome.

Код


Базовая настройка Selenium выглядит следующим образом (инициализация с помощью случайного user-agent):

from fake_useragent import UserAgentfrom selenium import webdriverfrom random import randrangeimport timeua = UserAgent(verify_ssl=False)user_agent = ua.randomprint("USER AGENT: " + user_agent)chrome_options = webdriver.ChromeOptions()chrome_options.add_argument("user-agent=" + user_agent)driver = webdriver.Chrome(chrome_options=chrome_options)

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

id = randrange(100000000000000)url = "https://www.papajohnsfeedback.com/GBR?GUID=" + str(id)print(url)driver.get(url)time.sleep(1)driver.find_element_by_id('NextButton').click()time.sleep(1)driver.find_element_by_id('NextButton').click()time.sleep(1)driver.find_element_by_xpath("//div[contains(@class, 'Opt1')]/span").click()time.sleep(1)

Часть скрипта бота

XPath


XPath это язык запросов для выбора узлов из документа HTML или XML. Для каждого из вопросов опроса я использовал инструмент тестирования XPath реального времени для выбора правильных узлов, на которые нажимает бот. Конечно, я ставил Papa Johns оценки 5 звёзд по всем пунктам.


Тестирование XPath

Всегда пожалуйста!


И, наконец, получаем код валидации.


Papa John's (с) Завершающая страница опроса

driver.find_element_by_id('NextButton').click()time.sleep(1)code = driver.find_element_by_class_name('ValCode').get_attribute("innerHTML").split(' ')[2]

Извлечение кода валидации

Празднование


Через полчаса программирования python-бот был готов. Вот gist с кодом, а вот он в действии:


Спасибо, Papa Johns


Я ввёл все сгенерированные коды при расчёте в корзине Papa John's. И вот он потенциально бесконечный запас чесночных пицца-палочек.

Бесплатная пицца для меня и отличные отзывы для отдела маркетинга Papa John's. Кажется, беспроигрышная ситуация! Для этого бота мне даже пришлось изучить XPath и отточить свои навыки Selenium.



Конечно, я ничего не заказал. Как честный человек, я уведомил Papa John's об уязвимости вместе с видеодоказательством. На момент написания статьи они не ответили. Но опрос больше не работает, так что я думаю, что они получили сообщение.

Кстати, я мог бы запрограммировать заказ 1000 чесночных пицц из каждого магазина Papa John's по всей Великобритании и в одиночку повергнуть сеть Papa John's в безумие. Можете себе представить, какой возник бы хаос?

Возможно, я сделал это в параллельной вселенной.
Подробнее..

Тестирование скриншотами

03.03.2021 12:06:10 | Автор: admin

Для будущих студентов курса Python QA Engineer подготовили авторскую статью.

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


Здравствуйте! Сегодня хочу рассказать о нашем опыте тестирования скриншотами с использованием python, selenium, и Pillow.

Зачем? У нас был довольно большой (~1000) набор тестов на стеке python, pytest, selenium, которые отлично проверяли, что кнопки кликаются, а статистика отправляется (с использованием browserup proxy), но пропускали баги типа таких:

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

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

Посмотрим, как selenium определяет видимость для элементов карусели:

Скрипт:

from selenium.webdriver import Chromefrom collections import Counterdriver = Chrome()driver.get("https://go.mail.ru/search?q=%D1%86%D0%B2%D0%B5%D1%82%D0%BE%D1%87%D0%BA%D0%B8%20%D0%BA%D0%B0%D1%80%D1%82%D0%B8%D0%BD%D0%BA%D0%B8")elements = driver.find_elements_by_css_selector(".SmackPicturesContent-smackImageItem")print(Counter([el.is_displayed() for el in elements]))driver.quit()

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

Аналогично будут работать и явные ожидания (visibility_of, visibility_of_all_elements_located, etc), так как они просто дергают у элемента is_displayed.

Решение

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

В общем это работает так:

  • открываем страницу селениумом, готовим ее к тесту (заполняем поля, жмем кнопки);

  • скриншотим всю страницу, и вырезаем кусок, на котором размещен компонент, который нужно протестировать;

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

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

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

Рабочий пример на гитхабе.

В итоге тест выглядит так:

def test_search_block(self):   self.driver.get("https://go.mail.ru/")   def action():       self.driver.find_element_by_xpath("//span[contains(text(), 'Соцсети')]").click()   self.check_by_screenshot((By.CSS_SELECTOR, ".MainPage-verticalLinksWrapper"), action=action)

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

После завершения теста у нас получится три изображения с тестинга, с прода и дифф. Все три добавляем в отчет.

Благодаря плагину для аллюра отчет выглядит так:

И в случае падения:

Проблемы

  1. Антиалайзинг особенно сильно флакали тесты, где на скриншот попадали svg. Решили смазав границу между цветами:

RED = "red"GREEN = "green"BLUE = "blue"ALPHA = "alpha"# https://github.com/rsmbl/Resemble.js/blob/dec5ae1cf1d10c9027a94400a81c17d025a9d3b6/resemble.js#L121# https://github.com/rsmbl/Resemble.js/blob/dec5ae1cf1d10c9027a94400a81c17d025a9d3b6/resemble.js#L981tolerance = {   RED: 32,   GREEN: 32,   BLUE: 32,   ALPHA: 32,}
def _is_color_similar(self, a, b, color):   """Проверить схожесть цветов. Для того, чтобы тесты не тригеррились на антиалиасинг допуски   в self.tolerance.   """   if a is None and b is None:       return True   diff = abs(a - b)   if diff == 0:       return True   elif diff < self.tolerance[color]:       return True   return False

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

Для особо тяжелых случаев просто удаляем проблемный элемент со страницы, например, так:

def test_search_block(self):   self.driver.get("https://go.mail.ru/")   def action():       element = self.driver.find_element_by_xpath("//span[contains(text(), 'Соцсети')]")       self.driver.execute_script("arguments[0].remove()", element)   self.check_by_screenshot((By.CSS_SELECTOR, ".MainVerticalsNav-listItemActive"), action=action)

Примерно так же можно добавить элементу заливку, удалить изображение, и т.д.

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

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

  3. Для мобильных тестов (андроид эмуляторы и эмуляция в хроме) нужно учитывать плотность пикселей. Так получаем размеры и координаты элемента:

def _get_raw_coords_by_locator(self, locator_type, query_string):   """Без учета плотности пикселей."""   wait = WebDriverWait(self.driver, timeout=10, ignored_exceptions=Exception)   wait.until(lambda _: self.driver.find_element(locator_type, query_string).is_displayed(),                   message="Невозможно получить размеры элемента, элемент не отображается")     el = self.driver.find_element(locator_type, query_string)   location = el.location   size = el.size   x = location["x"]   y = location["y"]   width = location["x"] + size['width']   height = location["y"] + size['height']   return x, y, width, height

А так делаем поправку на плотность пикселей:

def _get_coords_by_locator(self, locator_type, query_string) -> Tuple[int, int, int, int]:   x, y, width, height = self._get_raw_coords_by_locator(locator_type, query_string)   return x * self.pixel_ratio, y * self.pixel_ratio, width * self.pixel_ratio, height * self.pixel_ratio

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

Размер элементов, которые возвращает селениум:

from selenium.webdriver import Chrome, ChromeOptionsoptions = ChromeOptions()options.add_experimental_option("mobileEmulation", {'deviceName': "Nexus 5"})options.add_argument('--headless')caps = options.to_capabilities()driver = Chrome(desired_capabilities=caps)driver.get("https://go.mail.ru/")print(driver.find_element_by_xpath("//body").size)driver.save_screenshot("test.png")driver.quit()

Напечатает: {'height': 640, 'width': 360} для боди страницы.

И в тоже время вернет скриншот размером 1080 х 1920:

 file test.pngtest.png: PNG image data, 1080 x 1920, 8-bit/color RGBA, non-interlaced

4.Тесты никогда не будут зелеными во время релиза. Любые изменения в покрытых компонентах заставят тесты покраснеть, или сделают их сравнение невозможным (если расползутся размеры на тестинге и стейджинге). У этого есть и плюс: на диффе в отчете сразу видны изменения, в том числе те, которых не ожидали.

Итог

Сейчас у нас ~570 скриншот-тестов в двух сборках (мобильная и десктопная). Каждая сборка запущена в 20 потоков, и идет около 15 минут. С учетом изменений в релизах, которых ломают тесты, флакающих не больше 2-3%. В основном тесты падают из-за регресса, или изменений в верстке. Писать их тоже достаточно легко, при необходимости проверку скриншотами можно совместить с обычными для селениум-тестов проверками (текст, кликабельность, видимость).

Полезные ссылки про тестирование скриншотами:

  1. https://blog.rinatussenov.com/automating-manual-visual-regression-tests-with-python-and-selenium-be66be950196

  2. https://www.youtube.com/watch?v=crbwyGlcXm0


Узнать подробнее о курсе Python QA Engineer.

Смотреть открытый вебинар по теме Непрерывная интеграция с Jenkins.

Подробнее..

Госуслуги и запись на прием. Живая очередь?

26.04.2021 14:21:23 | Автор: admin

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

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

Решение довольно простое. Когда пользователь выбирает адрес на карте, отправляется запрос, который возвращает доступное время посещения. Он виден в консоле разработчика. Оттуда узнаем о URL и параметрах.

def send_post(cookies):    url = 'https://www.gosuslugi.ru/api/lk/v1/equeue/agg/slots'    headers = {'Content-type': 'application/json;charset=UTF-8', 'Accept':'application/json', 'Cookie':cookies}    payload = {'eserviceId':'','serviceId':[''],'organizationId':[''],'parentOrderId':'','serviceCode':'','attributes':[]}    return post(url, data=dumps(payload), headers=headers)

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

Для реализации понадобился Python, Selenium и планировщик заданий Windows. Таким образом, получаем следующий основной код:

from webbrowser import open as open_tabfrom selenium import webdriverfrom datetime import datetimefrom requests import postfrom json import dumpsfrom os import pathdef main():    response = send_post(read_cookies())    if response.status_code == 401:        write_cookies(get_cookies())        write_log('Ошибка 401. Обновлены куки.')        main()        return    elif response.status_code == 200:        length = len(response.json()['slots'])        if length > 0:            write_log('Есть мест: ' + length)            open_tab(TARGET_LINK, new=1)        else:             write_log('Нет мест')    else:        write_log('Ошибка {0}'.format(response.status_code))

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

def get_cookies():    options = webdriver.ChromeOptions()    options.add_argument('--no-sandbox')    options.add_argument('--minimal')    driver = webdriver.Chrome(executable_path=DRIVER_FILE, options=options)    driver.get('https://esia.gosuslugi.ru/')    driver.implicitly_wait(7)    input_login = driver.find_element_by_id('login')    input_password = driver.find_element_by_id('password')    btn_enter = driver.find_element_by_id('loginByPwdButton')    input_login.send_keys(LOGIN)    input_password.send_keys(PASSWORD)    btn_enter.click()    driver.get(TARGET_LINK)    cookies = driver.get_cookies()    driver.close()    return cookies

Для запроса, куки форматируются в имя=значение;

raw_cookies = ''.join(['{}={}; '.format(i['name'], i['value']) for i in cookies])

Осталось настроить планировщик заданий Windows. Прямой запуск скрипта .py у меня не получился. Поэтому через .bat одна команда python "script.py". Да, при этом открывается окно консоли. Есть внешние программы, позволяющие запустить консоль скрытно.

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

Подробнее..

Хамелеон, которого мы создали и приручили

01.12.2020 14:07:50 | Автор: admin

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


Его появлению предшествовало 15 лет практики тестирования в компании IBS AppLine* (лидера российского рынка аутсорсинга услуг тестирования по версии TAdviser за 2018 год на минуточку!). На базе этих знаний и экспертизы мы задались целью ускорить старт проектов, повысить качество тестирования, упростить введение в работу новичков. Решение должно позволить автоматизировать функциональное тестирование веб, мобильных, десктоп-приложений и различных видов API.




В общем, исследовательский центр IBS AppLine Innovation** суммировал весь опыт компании и создал Хамелеон инструмент для автоматизации функционального тестирования. Делался с использованием языка программирования Java и инструментов Cucucmber, Selenium, Appium, Winium, Spring. Этот фреймворк:


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

Теперь подробнее о функционале



Как устроен Хамелеон


Вот несколько особенностей нашего фреймворка:


  • Это многомодульный maven-проект, который включает модули тестирования web, мобильных приложений, SAP-приложений, баз данных, Rest API и web-сервисов. Необходимые модули подключаются к проекту.
  • Мы взяли проверенные временем оpen source-инструменты, в том числе Selenium, Appium, Winium, и удачно их объединили в одном решении.
  • Для ускорения разработки автоматизированных тестов мы создали плагин для среды разработки IntelliJ IDEA. Получился полезный инструмент разработчика автоматизированных тестов. Плагин дополняет возможности IDEA, делая ее полноценным рабочим местом.
  • Для удобства разработки автоматизированных тестов для web-приложений мы создали расширение для браузера Google Chrome, которое позволяет добавлять элементы тестируемого приложения в проект прямо из браузера и имеет возможность записи автоматизированного теста в формате Cucumber методом Record&Playback.

Open source-библиотеки


В основе инструмента лежат оpen source-библиотеки Selenium, Appium, Winium, UIAutomation. Для разработки самих тестов используется фреймворк Cucumber, который позволяет писать на русском языке. Такой формат понятен и ручным тестировщикам, и не имеющим отношения к написанию конкретного теста специалистам автоматического тестирования, что снижает порог вхождения сотрудников в проект. Всему, что происходит в Cucumber, соответствуют свои Java-команды, так что при необходимости тесты можно разрабатывать на чистой Java.



Простота установки


Для разработки автоматизированных тестов с использованием Java на рабочую станцию устанавливаются Java JDK, Apache Maven/Gradle, IntelliJ IDEA, плагины для Intellij IDEA, Git Client. У начинающих специалистов это занимает много времени. Мы упростили процесс, разработав общий инсталлятор, который представляет собой .exe-файл с возможностью выбора необходимого ПО для установки на рабочее место:




Начало разработки


Для разработки автоматизированных тестов можно использовать готовые стартеры проектов. Стартеры это архетипы maven, которые содержат готовую структуру проекта. Они хранятся во внутреннем репозитории компании. При создании проекта в IntelliJ IDEA нужно лишь выбрать необходимые. Например, для разработки тестов, которые взаимодействуют с web-приложением и REST, необходимо подключить модули chameleon-selenium-cucumber и chameleon-rest-cucumber.




Немного о фреймворке


В основном автоматизированные тесты разрабатываются с помощью инструмента Cucumber, который позволяет писать тесты на русском языке. Автоматизированный тест состоит из следующих блоков:


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

Пример автоматизированного теста:



# language: ru# Тестовые данные:  # $ФИО Иванов Иван Иванович  # $ссылка https://www.appline.ru/Функция: Заявка на обучение  Сценарий: Заявка на обучение    * страница "Главная" загружена    * выбран элемент коллекции "Меню" с параметрами:      | field        | operator | value             |      | Наименование | равно    | Start IBS AppLine |    * нажатием на кнопку "Наименование" загружена страница "Start IBS AppLine"    * поле "Имя" заполняется значением "#{ФИО}"    * поле "Ссылка на резюме" заполняется значением "#{ссылка}"    * выполнено нажатие на "Отправить"    * поле "Required field" видимо

Существуют шаблоны для работы с переменными: операции с датами, математические операции, выполнение кода и т.д. Например, для работы с текущей датой используется шаблон #now{дата; исходный_формат; смещение}. Предположим, в автоматизированном тесте необходимо проверить дату операции, которая была только что осуществлена. Такая проверка будет выглядеть так:


* значение поля "Дата операции" равно "#now{dd.MM.yyyy HH:mm}"

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


* поле "Дата операции" заполняется значением "#now{dd.MM.yyyy;+1d}}"

Выполнить программный код можно с использованием шаблона #script{RESULT=выражение_java}. Например, удаление лишних символов в переменной будет выглядеть следующим образом:


* в переменной "Номер_счета" сохранено значение поля "Номер счета"* значение поля "Номер счета" равно "#script{RESULT = Номер_счета.replaceAll("", "")}"

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


Например, авторизация в приложении может описываться в 3 шага:


Когда заполняются поля:  | field  | value        |  | Логин  | test@test.ru |  | Пароль | 123123       |И выполнено нажатие на "Войти"Тогда страница "Главная" загружена

Или на основе этих шагов создается 1 шаг (пароль в этом случае хранится в отдельном файле с пользователями):


Дано авторизован пользователь "test@test.ru"

Все размеченные элементы тестируемого приложения имеют свой тип, например, Button, TextInput, Combobox, Checkbox и т.д., это позволяет использовать одни и те же Cucumber-шаги для работы с разными типами элементов. Например, есть шаг:


* поле "field" заполяется значением "value"

Он сможет работать как c текстовыми полями, так и чекбоксами, выпадающими списками и т.д. Это позволяет при разработке автоматизированного теста не задумываться о заполнении полей этим занимается сам фреймворк, мы просто указываем ему заполнить поле.



АРМ тестировщика или разработка теста


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


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


Список тестов


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




Репозиторий объектов


Традиционный и очень популярный инструмент разработчика автоматизированных тестов шаблон Page Object Pattern. При его использовании элементы тестируемого приложения описываются в отдельных java-классах, которые соответствуют страницам тестируемого приложения. На крупных проектах таких Page Object-классов становится очень много. Кроме того, иногда они еще и называются беспорядочно. В результате для больших проектов такой подход не очень удобен: неизбежно дублирование работы на проектах работает много специалистов, приходится расшифровывать работу коллег и анализировать разметку элементов, а это не всегда проходит гладко.


Поэтому мы создали репозиторий объектов тестируемого приложения. Он хранит все его элементы не в классах, а в одном или нескольких xml-файлах, а плагин для IntelliJ Idea показывает эти xml-файлы в виде древовидной структуры, в которой узлами являются страницы тестируемого приложения. Вместо разномастных английских названий мы используем русский язык, что упрощает работу тестировщиков, так им легче находить размеченные страницы и элементы, а также добавлять новые.


Приведем конкретный пример. Для одного крупнейшего отечественного банка (название по понятным причинам не называем) нами было разработано 20 автотестов, использовавших 78 страниц тестируемых приложений (достаточно длинный бизнес-процесс). В обычных условиях тестировщикам пришлось бы создать 78 java-классов и разметить на них более 2000 элементов. С Хамелеоном мы всю эту громаду открываем в древовидной структуре, в которой их легко и просто просматривать, перетаскивать, компоновать.




Существует два способа добавления элементов в репозиторий.


Добавление в среде разработки IntelliJ IDEA:



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


Также есть возможность добавлять элементы прямо из тестируемого приложения с помощью созданного нами расширения для браузера Google Chrome. Расширение самостоятельно определяет тип элемента, его наименование и подставляет локатор. Для этого достаточно навести мышкой на элемент. Расширение синхронизируется с плагином в IntelliJ IDEA, поэтому все, что происходит в браузере, передается в среду разработки. Так можно наполнять репозиторий объектов, проходя ручной тест в браузере. С помощью расширения можно проверить корректность локатора уже существующего в репозитории элемента.




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



Разработка автоматизированного теста


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


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


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




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




Третий способ разработки автотеста (самый удобный и популярный) с помощью нашего расширения для браузера Google Chrome. Плагин записывает действия пользователя на странице тестируемого приложения и конвертирует их в автоматизированный тест.


Например, пользователь логинится на сайте, нажимая для этого на кнопку Логин. Рекордер записывает шаг * выполнено нажатие на поле Логин. Пользователь заполняет поле Логин значением User, рекордер записывает шаг * поле Логин заполняется значением User и так далее. После этого получившийся автотест можно скопировать и вставить в среду разработки для редактирования. Плагин автоматически, на основе объектного репозитория, определяет, на какой странице находится пользователь, и выполняет поиск размеченных элементов. Если элемента в репозитории нет, то будет предложено его добавить.


Пока рекордер существует только для браузера. В процессе разработки находится рекордер для SAP GUI.



Запуск и отладка


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


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



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


Чтобы отслеживать ход выполнения теста, мы разработали консоль, куда перенаправляется весь лог с IntelliJ IDEA. Это позволяет контролировать процесс и понимать, что сейчас выполняет автоматизированный тест. Консоль отображается поверх содержимого экрана и не мешает выполнению теста. Есть возможность настраивать формат вывода в консоль (размер, шрифт, цвет и т.д.).




Документация


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




Тестирование API


Для тестирования API (REST и SOAP) также используется объектный репозиторий. В этом случае страницами будут endpoint, а элементами заголовки и тело запроса.




Во фреймворке реализованы базовые действия с API, при наборе действий происходит автоподстановка параметров вызова.




Отчет о выполнении автоматизированных тестов


В качестве инструмента отчетности был выбран Allure. При запуске тестов из среды разработки IntelliJ IDEA аllure-отчет можно открыть прямо из плагина.


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



Результаты


Хамелеон помог нам не на словах, а на деле.


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


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


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


Наконец, мы получили еще одно преимущество на отечественном рынке: кодирование на русском языке легче освоить. Для этого у нас есть корпоративный университет, где мы обучаем будущих тестировщиков писать автотесты с нуля буквально за месяц. И в этом очень важную роль играет Хамелеон: с его помощью мы закладываем общую базу, на нем обязательно проводим несколько занятий по разработке тестов. Ежемесячно через этот курс проходит 5-10 человек, имеющих базовые знания языка программирования Java.



Планы на будущее


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





* IBS AppLine лидер российского рынка услуг тестирования и обеспечения качества программного обеспечения, разработчик решений для автоматизации тестирования. Компания была создана в 2001 году, в штате более 700 специалистов, общее количество проектов превысило 1500.


** IBS AppLine Innovation (ранее Аплана АйТи Инновации) была создана в декабре 2017 года как центр исследований и разработки компании IBS AppLine (ранее Аплана). Основу команды составили ее собственные опытные инженеры и разработчики.

Подробнее..

Ускоряем и стабилизируем автотесты на codeception selenium

22.11.2020 10:20:58 | Автор: admin
Как прогнать несколько часов автотестов за 5 минут и при этом, чтобы это было стабильно и не вызывало головной боли при каждой сборке? Без лишней воды и вступлений предоставляю вашему внимаю сборник костылей и подпорок элегантных архитектурных решений, без которых невозможно добиться высокой скорости и стабильности автотестов.


Входные данные: фреймворк codeception, сервер с selenium, код на PHP, который нужно протестировать.

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

  1. Первое и самое очевидное это запараллеливание выполнения тестов, точнее запуск в несколько потоков групп тестов.
  2. Заполнение полей форм с помощью javascript.
  3. Далее на третьем месте по списку, но не по значимости стоит такое явление как перезапуск только тех тестов, которые упали, а не всего набора тестов.
  4. Перезапуск сессии с selenium.
  5. Очистка контекста в браузерных тестах
  6. Перезапуск потока.

Теперь подробнее про каждый пункт.

Параллельный запуск


В codeception есть возможность поделить тесты на группы с помощью нотации @ group в phpdoc метода или же всего класса. После распределения таким образом тесты на группы происходит их запуск в отдельном процессе, с помощью https://robo.li/.
Как выполнить этот запуск смотрим тут https://robo.li/tasks/Testing/#codecept.
Небольшая подсказка: group($group) set group option. Can be called multiple times.

Сколько делать одновременных потоков каждый сам должен понять, все зависит от ресурсов сервера, на котором крутятся тесты. На нашем проекте может одновременно крутиться десятки потоков.
Нужно не забыть перезапускать упавшие тесты также в несколько потоков. Если этого не делать, то будет ситуация когда прогон тестов из 10 потоков прошедший за 5 минут будет ждать перезапуска, например, 3 тестов, которые в один поток могут выполняться больше минуты.

Заполнение форм через Javascript


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

Мы переписали метод fillField библиотеки codeception.
Собственно вот часть кода, отражающая, суть:
$webDriver->executeJS(element = document.querySelector(arguments[0]);element.value = arguments[1],[$selector, $value])

Сами причины почему selenium долго заполняет поля текстом не ясны, к сожалению

Перезапуск упавших тестов


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

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


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

Перезапуск сессии с selenium



Когда у вас запущено много потоков, а сервер с selenium один и он уставший, может проскальзывать ошибка под названием Session timed out or not found, и тут нам помогает перезапуск сессии с selenium.

Вот код который делает перезапуск сессии:
/** @var WebDriver $webDriver */$webDriver = $I->getWebDriver();try {$I->amOnPage('/robots.txt');} catch (\Facebook\WebDriver\Exception\WebDriverException $e) {try {$I->comment('Что-то с сессией в селениуме - пытаюсь перезапустить сессию');$webDriver->debugWebDriverLogs();$webDriver->_restart();} catch (\Throwable $e) {$I->comment('Омайгад перезапустить не удалось, просто создаю новую сессию');$webDriver->_initializeSession();}$I->amOnPage('/robots.txt');}

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

Очистка контекста в браузерных тестах


Чтобы очистить контекст текущей сессии браузера в selenium нужно сделать запрос на любую другую страницу, чтобы поменялась структура документа. Чтобы не нагружать серверы ненужным рендерингом, запрос делается на любой текстовой файл, и так сложилось, что этим файлом у нас стал robots.txt.
Собственно зачем делать эту так называемую очистку контекста? Она нужна для того, чтобы тесты не смогли использовать разметку страницы из предыдущего теста. Часто бывало такое, что браузер находил элементы, которых нет на текущей странице или же, наоборот, не находил то, что нужно было найти и падал с ошибкой. Это происходит из-за того, что сценарий тестов на PHP не дожидается загрузки новой страницы и начинает делать всякие seeElement, waitForText в уже имеющейся(старой) структуре документа и в результате мы не понимаем, что произошло и почему упал тест. И тут то нас спасает предварительный запрос на robots.txt, после него мы имеем чистое место для начала нового тестового сценария.

Перезапуск потока



Проблемы с запуском и прохождением тестов могут быть не только на стороне сервера selenium (вот это неожиданность), но и на исполняющей стороне, там где запущен php процесс, обычно это CI сервер.
Так как на CI сервере происходит очень много всего, то по разным причинам php процессы, внезапно, могут обрываться и тем самым у нас получится что целая группа тестов останется не пройденной и придется ждать ее отдельного перезапуска. Тут к нам спешат на помощь Чип и Дейл проверка состояния процесса и его перезапуск если он вдруг завершится.
Чтобы сделать перезапуск процесса пришлось переопределить метод \Robo\Task\Base\ParallelExec::run
Вот отрывок кода:
if (!$process->isRunning()) {$doRerun = $this->doRerunCallback;$nbRerun = $nbRerunForProcess[$process->getCommandLine()] ?? 0;if ($doRerun($process) && $nbRerun <= 3) {$this->printTaskInfo('Try to rerun ' . $nbRerun . ' time ' ." for {command}: \n\n{output} \nend output for rerun",['command' => $process->getCommandLine(),'output' => $process->getOutput(),'_style' => ['command' => 'fg=white;bg=magenta'],]);$nbRerun++;$nbRerunForProcess[$process->getCommandLine()] = $nbRerun;$process->start();} else {

В этом методе уже есть проверка состояния процесса и нам нужно дописать десяток строк, чтобы этот процесс перезапускался по какому то условию, условие лежит в $this->doRerunCallback, это функция обратного вызова, в которой мы определяем нужно ли делать перезапуск. В ней у нас лежит следующее:
$parallel->setDoRerunCallback(function (Process $process) use ($self) {$group = $self->getGroupFromCmd($process->getCommandLine());if ($group) {return !$self->checkGroupReportExist($group);}return false;});

Т.е. по сути там проверка есть ли отчет по данной группе тестов или нет.

Итог


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


Подробнее..
Категории: Php , Selenium , Codeception

Перевод Автоматизация тестирования приложений Salesforce

27.11.2020 18:11:38 | Автор: admin

Если вы когда-либо пробовали использовать Selenium для автоматизации тестирования приложения Salesforce, вы, вероятно, знаете, насколько это непросто.

Selenium это потрясающий инструмент для автоматизации тестирования браузерных приложений. Однако приложения Salesforce более сложные по сравнению с большинством веб-приложений, поскольку в них используются теневые DOM и динамические элементы, включая iframe.

Без надлежащего фреймворка с большим количеством собственного кода автоматизация тестирования Salesforce превращается в настоящий кошмар!

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

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

Динамические элементы

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

Для приложений на базе Salesforce такое поведение является нормой.

В одном прогоне может быть идентификатор gino1, а в следующем уже gabagool5.

Обнаружить какую-либо логику или закономерность смены имен попросту невозможно. Имена изменяются динамически и непредсказуемым образом.

Что же нам делать?

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

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

Элементы iframe

Элементы iframe (встроенные фреймы) представляют собой HTML-документы, внедренные в другую HTML-страницу.

Элементы iframe используются для вставки на веб-страницу контента из внешнего источника, например проигрывателя подкастов:

С iframe сложно работать, потому что системе Selenium необходимо идентифицировать элементы во фрейме, что не всегда является простой задачей.

Не каждый инженер способен написать код для обработки такой ситуации.

Selenium умеет переключаться между несколькими iframe с помощью команды драйвера switchTo().frame.

//Store the web elementWebElement iframe = driver.findElement(By.cssSelector("#modal>iframe"));//Switch to the framedriver.switchTo().frame(iframe);//Now we can click the buttondriver.findElement(By.tagName("button")).click();

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

Помимо iframe есть еще кое-что, что может вызвать затруднения при автоматизации тестирования это теневые DOM.

Теневые DOM

Одним из популярных трендов в современной разработке веб-компонентов является применение теневых DOM.

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

Разработчики также часто добавляют собственные HTML-теги.

Selenium не может напрямую идентифицировать подобные HTML-теги в теневой DOM.

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

Существуют плагины, которые могут помочь в этом деле, например shadow-automation-selenium.

Но опять же, их применение требует дополнительных усилий. Вам придется добавить библиотеку в POM-файл системы Maven и выучить необходимый синтаксис.

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

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

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

Как вы смотрите на вариант решить описанные проблемы с помощью бесплатного решения для тестирования?

Я как раз нашел одно такое решение оно называется TestProject.

Тестирование приложений Salesforce с помощью TestProject

Эльдар Кравецкий (Eldar Kravetsky), технический директор и соучредитель TestProject, сказал мне, что приложения на базе Salesforce послужили отличным предметом для экспериментов при разработке нового улучшенного рекордера тестовых сценариев. Разработчики TestProject поняли, что если они смогут автоматизировать тестирование Salesforce, то их рекордер, вероятно, справится с автоматизацией чего угодно.

Многие проблемы, связанные с автоматизацией тестирования Salesforce и решаемые с помощью TestProject, также относятся и к другим приложениям, разработанным в codeless-средах, таких как SAP, ServiceNow и др.

Как я упоминал ранее, можно написать свои собственные тесты в Selenium или же непосредственно в TestProject, но не все обладают нужными навыками.

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

Итак, каким образом TestProject справляется с упомянутыми выше проблемами, которые присущи приложениям типа Salesforce?

Как TestProject работает с iframe?

В Selenium есть концепция контекста.

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

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

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

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

TestProject построен на базе Selenium и Appium, поэтому он умеет использовать методы API Selenium и расширять их без вмешательства со стороны пользователя.

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

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

На каждом шаге доступны расширенные настройки, в которых можно увидеть, существует ли какой-либо дополнительный контекст, например iframe.

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

Как TestProject работает с теневыми DOM?

Новый рекордер TestProject также умеет обрабатывать теневые DOM, активно используемые в Salesforce и других приложениях.

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

В большинстве случаев рекордер TestProject обрабатывает подобные ситуации за вас автоматически.

Рекордер TestProject обрабатывает все операции взаимодействия с теневой DOM в фоне вам ни о чем не приходится беспокоиться.

Как TestProject работает с динамическими элементами?

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

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

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

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

Весьма впечатляюще!

Почему полезно использовать TestProject?

Если вы умеете программировать, то можете написать любую тестовую программу.

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

Но опять же, для этого нужны навыки, которыми обладает не каждый.

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

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

Почему бы не сэкономить немного времени и не ломать голову понапрасну?Попробуйте это решение и поделитесь своими мыслями.


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

- Python QA Engineer

- Java QA Engineer

- Game QA Engineer

- QA Engineer (Базовый курс)


Подробнее..

Перевод Тестирование в Puppeteer vs Selenium vs Playwright сравнение производительности

29.01.2021 10:18:54 | Автор: admin

Ранее мы уже писали о том, когда бывает нужна автоматизация тестирования и какие проверки при этом используют. Сегодня предлагаем обсудить использование инструментов на практике и оценить их производительность. С разрешения Giovanni Rago автора серии полезных материалов о тестировании мы перевели его статью Puppeteer vs Selenium vs Playwright: сравнение скорости (Puppeteer vs Selenium vs Playwright, a speed comparison). Статья будет интересна тем, кто задумывается о выборе подходящего инструмента автоматизации в своих проектах.

От автора:

Для разработки системы мониторинга и тестирования Checkly мы решили использовать Puppeteer. Это инструмент автоматизации тестирования с возможностью включения headless браузера и открытым исходным кодом, позже мы также внедрили Playwright. Checkly помогает узнать, работает ли тестируемый сайт так, как ожидается в определенный момент времени. В нашем случае основной интерес вызывала скорость работы инструмента.

Задача определения наиболее быстрого инструмента автоматизации не так проста. Поэтому мы решили провести свой бенчмарк тест производительности, чтобы сравнить новичков Puppeteer и Playwright с ветераном WebDriverIO (в связке с Selenium и протоколом автоматизации DevTools).

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

Почему мы сравниваем эти инструменты автоматизации?

Рассматривать Puppeteer/Playwright и Selenium это всё равно что сравнивать яблоки с апельсинами: инструменты имеют существенно разные возможности и применяются в разных областях автоматизации, и тот, кто их оценивает, должен учитывать это, прежде чем анализировать скорость.

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

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

Опыт показывает, что большинство пользователей Selenium, которые работают с JavaScript, используют WebDriverIO для запуска автоматизированных скриптов. Именно поэтому мы выбрали его. Также нам было интересно протестировать новый DevTools режим.

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

Методология, или как мы запускали тесты

Можете смело пропустить этот раздел, если хотите сразу перейти к результатам. Но мы рекомендуем всё же ознакомиться с ним. Это поможет лучше понять результаты тестов.

Общие рекомендации

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

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

  2. Простое выполнение: скрипты запускались так, как это было показано в документации к каждому инструменту и с минимальными конфигурациями. Например, для Playwright node script.js

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

  4. Самые свежие версии: для тестирования всех инструментов мы использовали их последние версии.

  5. Одинаковый браузер: все скрипты выполнялись в headless Chromium.

В следующем разделе представлена дополнительная информация по всем пунктам.

Техническая настройка

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

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

Все тесты мы проводили на MacBook Pro 16 последнего поколения под управлением macOS Catalina 10.15.7 (19H2) со следующими характеристиками:

Модель: MacBookPro16,1

Процессор: 6-Core Intel Core i7

Скорость процессора: 2,6 ГГц

Количество процессоров: 1

Количество ядер: 6

Кэш-память L2 (на ядро): 256 Кб

Кэш-память L3: 12Мб

Технология Hyper-Threading: включена

Память: 16 Гб

Мы использовали следующие зависимости:

bench-wdio@1.0.0 /Users/ragog/repositories/benchmarks/scripts/wdio-selenium

@wdio/cli@6.9.1

@wdio/local-runner@6.9.1

@wdio/mocha-framework@6.8.0

@wdio/spec-reporter@6.8.1

@wdio/sync@6.10.0

chromedriver@87.0.0

selenium-standalone@6.22.1

scripts@1.0.0 /Users/ragog/repositories/benchmarks/scripts

playwright@1.6.2

puppeteer@5.5.0

Скрипты, которые мы использовали вместе с результатами их выполнения, вы можете найти в GitHub-репозитории.

Измерения

Мы получили следующие показатели, все рассчитано на основе 1000 прогонов:

* Среднее время выполнения (в секундах).

* Стандартное отклонение (в секундах): показатель разброса времени выполнения.

* Коэффициент вариации (CV): безразмерный коэффициент, который показывает отклонение результатов от среднего.

* P95 (изменение 95-го процентиля): наибольшее значение, оставшееся после отбрасывания верхних 5% отсортированного списка полученных данных. Интересно было бы узнать, как выглядит не экстремальное, но все еще высокое значение.

Что мы не измерили (пока)

* Надежность: ненадежные сценарии быстро становятся бесполезными, независимо от того, насколько быстро они выполняются.

* Эффективность распараллеливания: параллельный запуск очень важен в контексте инструментов автоматизации. Однако в этом случае мы в первую очередь хотели понять, с какой скоростью может выполняться один скрипт.

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

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

Результаты

Ниже вы видите совокупные результаты тестирования производительности. Полные наборы данных вы можете найти в репозитории GitHub.

Запуск на демосайте

Первый бенчмарк запускался на нашем демонстрационном сайте. Эта веб-страница, размещенная на Heroku, создана на основес использованием Vue.js и использует бэкенд на Express. В большинстве случаев данные с бэкенда фактически не извлекаются. Вместо этого фронтенд хранит данные на стороне клиента.

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

Общие результаты выглядят следующим образом:

Результаты бенчмарка для сценария быстрого входа на нашем демосайтеРезультаты бенчмарка для сценария быстрого входа на нашем демосайте

Первое, что обращает на себя внимание это большая разница между средним временем выполнения для Playwright и Puppeteer, причем последний почти на 30% быстрее и демонстрирует меньший разброс в его производительности. Мы задумались, не связано ли это с более длительным запуском со стороны Playwright. Мы не стали рассматривать этот и аналогичный вопросы, во избежание увеличения объема работ для первого бенчмарка.

Playwright vs PuppeteerPlaywright vs Puppeteer

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

WebDriverIO with WebDriver vs WebDriverIO with DevToolsWebDriverIO with WebDriver vs WebDriverIO with DevTools

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

Puppeteer vs WebDriverIO with DevToolsPuppeteer vs WebDriverIO with DevTools

Запуск на реальном веб-приложении

Предыдущий опыт научил нас, что разницу между демонстрационной средой и реальным миром почти всегда недооценивают. Поэтому мы очень хотели, чтобы тесты производительности выполнялись на рабочем приложении. В этом случае мы выбрали Checkly для запуска интерфейса Vue.js и бэкэнда, который в значительной степени использует AWS.

Запущенный нами скрипт очень похож на классический тест E2E: вход в Checkly, настроенная проверка API, сохранение и тут же удаление. Мы с нетерпением ждали этого сценария, но у каждого из нас были разные ожидания относительно того, как будут выглядеть цифры.

Результаты бенчмарка для нашего проверочного сценария ChecklyРезультаты бенчмарка для нашего проверочного сценария Checkly

В этом случае разница во времени выполнения между Playwright и Puppeteer почти исчезла. Playwright теперь выходит на первое место и демонстрирует немного меньший разброс.

Playwright vs PuppeteerPlaywright vs Puppeteer

Разница между новыми инструментами и обеими разновидностями WebDriverIO тоже соответственно меньше. Стоит отметить, что последние два теперь дают больший разброс в результатах по сравнению с предыдущим сценарием, в то время как Puppeteer и Playwright показывают меньшие отклонения.

Playwright vs WebDriverIO with SeleniumPlaywright vs WebDriverIO with Selenium

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

Теперь мы отступим назад и сравним время выполнения в разных сценариях:

Среднее время выполнения тестовых сценариевСреднее время выполнения тестовых сценариев

Сомневаетесь в результатах? Запустите свой собственный бенчмарк! Вы можете использовать наши сценарии тестирования, представленные выше. Не уверены в настройке? Не стесняйтесь сообщать об этом, чтобы сделать сравнение лучше.

Заключение

Прежде всего, давайте рассмотрим инструменты от самых быстрых к самым медленным для обоих сценариев тестирования:

Рейтинг производительностиРейтинг производительности

Наше исследование производительности позволило сделать несколько интересных выводов:

  • Хотя Puppeteer и Playwright используют сходные API, похоже, что Puppeteer имеет значительное преимущество в скорости на более коротких скриптах (по нашим наблюдениям, выигрыш составляет около 30%).

  • Скрипты Puppeteer и Playwright показывают более быстрое время выполнения (около 20% в сценариях E2E) по сравнению с вариантами Selenium и DevTools WebDriverIO.

  • Протоколы автоматизации WebDriverIO, WebDriver и DevTools показали сопоставимое время выполнения.

Выводы

  • При запуске большого количества более быстрых сценариев, если нет необходимости в кросс-браузерности, возможно, стоит запустить Puppeteer, чтобы сэкономить время. В более длинных сценариях E2E разница сводится к минимуму.

  • Имеет смысл подумать, необходим ли вам запуск установки большего количества barebone-систем. Возможно, что удобство дополнительных инструментов WebDriverIO стоит того, чтобы потратить немного больше времени на ожидание результатов.

  • Колебания времени выполнения могут не иметь большого значения в бенчмарках, но в реальном мире они могут накапливаться и замедлять сборку. Помните об этом при выборе средства автоматизации.

  • Глядя на прогресс с обеих сторон, мы задаемся вопросом, выйдет ли в будущем DevTools на передний план или WebDriver будет продолжать играть центральную роль в автоматизации браузеров. Мы предлагаем обратить внимание на обе технологии.

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

Спасибо за внимание! Надеемся, что перевод был вам полезен.

Подробнее..

Перевод Cypress VC Selenium

17.06.2021 18:06:38 | Автор: admin

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

Вот вам вопрос на миллион долларов: является ли Cypress чем-то большим, чем платформа для автоматизации веб-тестов и может ли он заменить Selenium?

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

Краткое описание

Cypress - это тестовая веб-платформа нового поколения. Она была разработана на основе Mocha и Chai и представляет собой платформу для сквозного тестирования на основе JavaScript.

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

Архитектура

Cypress работает в одном цикле выполнения приложения. Node.js. является серверным методом, лежащим в основе Cypress. Процессы Cypress и Node.js постоянно взаимодействуют, синхронизируются и совместно выполняют задачи. Кроме того, Cypress работает на сетевом уровне, интерпретируя и изменяя веб-трафик. Это позволяет Cypress не только редактировать все браузеры, но и обновлять коды, которые могут повлиять на автоматизацию браузеров.

В Selenium, когда мы запускаем сценарий автоматизации Selenium, клиентская библиотека Selenium взаимодействует с Selenium API, который отправляет команду привязки драйверу браузера с помощью проводного протокола JSON. Драйвер браузера использует реестр HTTP для фильтрации всех команд управления HTTP-запроса и HTTP-сервера. Затем команды браузера выполняются в скрипте selenium, а HTTP-сервер отвечает скрипту автоматизированного тестирования.

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

Установка

В Cypress нет никакой предварительной настройки, просто установите файл.exe и автоматически настройте все драйверы и зависимости. Автоматизация может быть выполнена за считанные минуты. Одной из философий дизайна Cypress было сделать весь процесс тестирования удобным и комфортным для разработчиков, упаковки и объединения.

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

Если мы также примем во внимание время и сложность имплементации, здесь Cypress имеет преимущество над Selenium.

Поддерживаемые языки

Cypress поддерживает только JavaScript. Поддержка других языков отсутствует, что иногда вынуждает пользователя изучать определенный язык сценариев.

Selenium, в свою очередь, поддерживает широкий спектр языков Java, C#, Python, Ruby, R, Dar, Objective-C, Haskell и PHP, а также JavaScript.

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

Кросс-браузерная поддержка

Cypress поддерживает Canary, Chrome, Chromium, Edge, Edge Beta, Edge Canary, Edge Dev, Electron, Firefox (бета-поддержка), Firefox Developer Edition (бета-поддержка), Firefox Nightly (бета-поддержка).

Selenium поддерживает почти все основные браузеры на рынке, что является дополнительным преимуществом Selenium. Ниже приведен список поддерживаемых браузеров: Chrome (все версии), Firefox (54 и новее), Internet Explorer (6 и новее), Opera (10.5 и новее), Safari (10 и новее).

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

Параллельное исполнение пакета автоматизированного тестирования

По сравнению с Selenium в параллельной проверке, cypress отстает.

Selenium имеет много вариантов для параллельного исполнения, что для автоматизации тестирования очень важно. Grid широко используется в сообществе QA с TestNG для параллельного исполнения. А контейнеризация Docker может быть быстро интегрирована.

Производительность

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

Selenium имеет несколько уровней кода между тестом и браузером, в чем, например, Cypress проигрывает.

Интеграция процессов автоматизации с CI/CD

Cypress: Возможна, но ограничена. Существует только одна альтернатива для командной строки и библиотеки npm - Mocha. Служба CI должна поддерживать npm, а запись тестов для большинства записей на сервере CI будет платной.

Selenium: Возможно применение CI/CD. Любая библиотека тестов, их запись и шаблоны исполнения могут использоваться и быстро адаптироваться к требованиям.

Лицензирование

Cypress также доступен под лицензией MIT как open-source. Однако, если сравнивать его с Selenium, все функции Cypress не будут бесплатными, например, приборная панель Cypress бесплатна для seed и платна для Sprout, Tree и Forest. (https://www.cypress.io)

Selenium разрешен под лицензией Apache 2.0, правообладатель Software Freedom Conservation.

Поддержка ОС

Cypress: Windows, Mac, Linux

Selenium: Windows, Linux, Mac, Android, iOS

Поддержка BDD и DataDrivenTesting

Selenium поддерживает BDD и data-driven с помощью внешних библиотек, что в Cypress невозможно.

Локаторы для идентификации объектов

Cypress поддерживает только CSS и Xpath.

Cypress поддерживает все виды веб-локаторов, такие как ID, Name, XPath, селекторы CSS, текстовые ссылки, текстовые частичные ссылки и т.д.

Отчет о выполнении

Selenium: Extent, Allure и любые другие дашборды могут быть реализованы в наборах автоматизации.

Cypress: Дашборд - это как раз Cypress.

Окончательный вердикт

Selenium больше ориентирован на специалистов по автоматизации тестирования, а Cypress - на разработчиков для повышения эффективности TDD. Selenium был представлен в 2004 году, поэтому у него больше поддержки экосистемы, чем у Cypress, который был разработан в 2015 году и продолжает расширяться. Когда мы используем Selenium, в браузере можно манипулировать несколькими различными опциями, такими как Cookies, Local Save, Screen, Sizes, Extensions, Cypress control line options, но опция сети может быть обработана только при использовании Cypress.

Однако, вот некоторые из преимуществ, о которых заявляет Cypress:

1. Программа будет делать скриншоты во время проведения экспериментов. Затем мы можем просмотреть каждый запрос в качестве разработчика теста на панели Test Runner, чтобы увидеть, что произошло на каждом этапе.

2. Для тестирования с помощью Cypress не требуется никакого ожидания или режима сна. Прежде чем продолжить, он немедленно ожидает команд и утверждений.

3. Подобно модульным тестовым ситуациям, "шпионы" Cypress могут подтверждать и проверять действия функций, ответы сервера или таймеры по времени. С помощью Cypress вы можете заглушить сетевой трафик и персонализировать вызовы API в соответствии с вашими потребностями.

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


В преддверии старта курса "Java QA Engineer. Professional" приглашаем всех желающих на бесплатный двухдневный интенсив, в рамках которого мы рассмотрим CI- и CD- процессы, изучим основные инструменты и ключевые понятия (Server, agents, jobs. Fail fast, Scheduling, WebHooks). Подробно познакомимся с программной системой Jenkins и научимся интегрировать ее с git и Docker.

Записаться на интенсив:

Подробнее..

Перевод Как найти все битые ссылки на странице с помощью Selenium

02.06.2021 14:09:29 | Автор: admin

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

Теперь с помощью этого java-кода вы можете проверить все ссылки. Эти ссылки могут быть ссылками pdf, изображения, видео или фотографии.

Шаг 1: В HTML мы связываем ссылки с помощью этого кода: <a href="Adress"></a> это означает, что мы должны собрать все ссылки на веб-странице на основе <a>. Для этого мы используем этот код:

List<WebElement> allLinks = driver.findElements(By.tagName(LINKS_TAG));

LINKS_TAG - это "a". В конце страницы я добавлю весь код.

Шаг 2: Определение и проверка URL-адреса

String urlLink = link.getAttribute(LINKS_ATTRIBUTE);

LINKS_ATTRIBUTE - это "href"

Шаг 3: Отправка HTTP-запроса и считывание кодов HTTP-ответов

Мы создаем HttpConnection с параметром URL. Я добавил также Connection Timeout.

URL url = new URL(urlLink);HttpURLConnection httpURLConnect=(HttpURLConnection)url.openConnection();httpURLConnect.setConnectTimeout(5000);httpURLConnect.connect();
  • Информационные коды ответов: 100-199

  • Коды успешного ответа: 200-299

  • Редирект коды: 300-399

  • Коды ошибок клиента: 400-499

  • Коды ошибок сервера: 500-599

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

import org.openqa.selenium.By;import org.openqa.selenium.WebDriver;import org.openqa.selenium.WebElement;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.chrome.ChromeOptions;import org.testng.annotations.AfterClass;import org.testng.annotations.BeforeTest;import org.testng.annotations.Test;import java.net.HttpURLConnection;import java.net.URL;import java.util.List;public class FindAllBrokenLinks {    public final String DRIVER_PATH = "Drivers/chromedriver";    public final String DRIVER_TYPE = "webdriver.chrome.driver";    public WebDriver driver;    public final String BASE_URL = "https://www.bbc.com/";    public final String LINKS_ATTRIBUTE = "href";    public final String LINKS_TAG = "a";    @BeforeTest    public void beforeTest(){        ChromeOptions options = new ChromeOptions();        options.addArguments("--disable-notifications","--ignore-certificate-errors","--disable-extensions");        System.setProperty(DRIVER_TYPE,DRIVER_PATH);        driver = new ChromeDriver(options);        driver.manage().window().maximize();        driver.get(BASE_URL);    }    @Test    public void FindAllBrokenLinks() throws Exception{        List<WebElement> allLinks = driver.findElements(By.tagName(LINKS_TAG));        for(WebElement link:allLinks){            try {                String urlLink = link.getAttribute(LINKS_ATTRIBUTE);                URL url = new URL(urlLink);                HttpURLConnection httpURLConnect=(HttpURLConnection)url.openConnection();                httpURLConnect.setConnectTimeout(5000);                httpURLConnect.connect();                if(httpURLConnect.getResponseCode()>=400)                {                    System.out.println(urlLink+" - "+httpURLConnect.getResponseMessage()+"is a broken link");                }                else{                    System.out.println(urlLink+" - "+httpURLConnect.getResponseMessage());                }            }catch (Exception e) {            }        }    }    @AfterClass    public void CloseDriver(){        driver.close();    }}

Я использовал URL веб-страницы BBC в качестве базового URL, но запуск этого кода занял 1 минуту и 49 секунд. :) Возможно, вам стоит выбрать другой сайт.

Вот некоторые результаты тестов:

https://www.bbc.com/sport OK

https://www.bbc.com/reel OK

https://www.bbc.com/worklife OK

https://www.bbc.com/travel Временно приостановил работу

https://www.bbc.com/future OK

https://www.bbc.com/culture OK

https://www.bbc.com/culture/music OK

http://www.bbc.co.uk/worldserviceradio/ Не доступен

http://www.bbc.co.uk/programmes/p00wf2qw Не доступен

https://www.bbc.com/news/world-europe-57039362 OK


Перевод подготовлен в рамках набора учащихся на курс "Java QA Automation Engineer". Если вам интересно узнать о курсе подробнее, а также познакомиться с преподавателем, приглашаем на день открытых дверей онлайн.

Подробнее..

Перевод Кросс-браузерное тестирование в Selenium

11.06.2021 02:20:41 | Автор: admin

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

К концу этой статьи вы узнаете об определении кросс-браузерного тестирования, его преимуществах и работе с ним в Selenium и TestProject.

Примечание: Код из этой статьи находится на GitHub здесь.

Что такое кросс-браузерное тестирование?

Кросс-браузерное тестирование гарантирует, что наше тестируемое приложение (AUT) совместимо с каждым браузером, операционной системой и устройством. Цель заключается в сравнении ожидаемого поведения приложения в различных случаях. Бывают случаи, когда один и тот же тестовый сценарий проходит на одном или нескольких экземплярах, но не работает на другом экземпляре.

Возможно, сбой произошел из-за нашего тестового скрипта или приложения. Вы когда-нибудь пытались открыть веб-сайт с помощью Internet Explorer, но он не работал, а затем тот же сайт без проблем открывался в Chrome? Такие проблемы выявляются во время кросс-браузерного тестирования, поскольку данные из AUT отображаются по-разному в каждом браузере.

Преимущества кросс-браузерного тестирования

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

Я сосредоточусь на двух преимуществах кросс-браузерного тестирования:

  1. Время

  2. Тестовое покрытие

Время

Создание и выполнение индивидуального сценария тестирования (Test Script) для уникальных сценариев занимает много времени. Поэтому наши тестовые сценарии создаются с тестовыми данными для использования их комбинаций. Один и тот же сценарий тестирования может выполняться на Chrome и Windows для первой итерации, затем на Firefox и Mac для второй итерации, а затем на других сценариях для последующих итераций.

Это экономит время, поскольку мы создаем только один тестовый сценарий, а не несколько. Ниже приведены 2 фрагмента кода для загрузки и получения заголовка для страницы TestProject. Один пример - это кросс-браузерное тестирование, а другой пример содержит отдельные тестовые сценарии для трех браузеров (Chrome, Firefox и Edge).

package tutorials.testproject;import io.github.bonigarcia.wdm.WebDriverManager;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.edge.EdgeDriver;import org.openqa.selenium.firefox.FirefoxDriver;import org.testng.annotations.Parameters;import org.testng.annotations.Test;public class CrossBrowserTesting {  WebDriver driver;  @Test  @Parameters ( {"BrowserType"} )  public void testExamplePageOnMultipleBrowsers (String browserType) {    if (browserType.equalsIgnoreCase("Chrome")) {      WebDriverManager.chromedriver().setup();      driver = new ChromeDriver();    }    else if (browserType.equalsIgnoreCase("Edge")) {      WebDriverManager.edgedriver().setup();      driver = new EdgeDriver();    }    else if (browserType.equalsIgnoreCase("Firefox")) {      WebDriverManager.firefoxdriver().setup();      driver = new FirefoxDriver();    }    driver.manage().window().maximize();    driver.get("https://example.testproject.io/web/index.html");    System.out.println(browserType + ": " + driver.getTitle());  }}
package tutorials.testproject;import io.github.bonigarcia.wdm.WebDriverManager;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.edge.EdgeDriver;import org.openqa.selenium.firefox.FirefoxDriver;import org.testng.annotations.Test;public class IndividualBrowserTesting {  WebDriver driver;  @Test  public void testExamplePageOnMultipleBrowsersOnChrome () {    WebDriverManager.chromedriver().setup();    driver = new ChromeDriver();    driver.manage().window().maximize();    driver.get("https://example.testproject.io/web/index.html");    System.out.println("Chrome: " + driver.getTitle());  }  @Test  public void testExamplePageOnMultipleBrowsersOnFirefox () {    WebDriverManager.firefoxdriver().setup();    driver = new FirefoxDriver();    driver.manage().window().maximize();    driver.get("https://example.testproject.io/web/index.html");    System.out.println("Chrome: " + driver.getTitle());  }  @Test  public void testExamplePageOnMultipleBrowsersOnEdge () {    WebDriverManager.edgedriver().setup();    driver = new EdgeDriver();    driver.manage().window().maximize();    driver.get("https://example.testproject.io/web/index.html");    System.out.println("Chrome: " + driver.getTitle());  }}

Тестовое покрытие

Тестовое покрытие - это техника, которая определяет, что и в каком объеме покрывается в наших тестовых сценариях.

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

  • Что будет включено в наши сценарии тестирования, зависит от требований.

  • То, сколько охвачено в наших сценариях тестирования, зависит от браузеров и их различных версий.

Тестовое покрытие является эффективным мерилом для процесса тестирования. Однако 100% покрытие трудно обеспечить, и, скорее всего, функция ведет себя не так, как это обычно происходит в конкретной версии.

Как осуществить кросс-браузерное тестирование в Selenium?

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

Тестовые данные могут храниться в файле Excel, CSV, файле свойств, XML или базе данных. Мы также можем объединить TestNG с тестовыми данными для проведения тестирования на основе данных или кросс-браузерного тестирования. Для тестирования на основе данных аннотация DataProvider и атрибут dataProvider или атрибут dataProviderClass позволяют нашему тестовому сценарию получать неограниченное количество значений.

Когда речь идет о кросс-браузерном тестировании, мы можем использовать тег параметра и аннотацию Parameters для передачи различных имен браузеров. Ниже приведены фрагменты кода, отображающие XML-файл с тегом параметра и тестовый сценарий с аннотацией Parameters.

<suite name="Cross Browser Testing">    <test name = "Test On Chrome">        <parameter name = "BrowserType" value="Chrome"/>            <classes>                <class name = "tutorials.testproject.CrossBrowserTesting"/>            </classes>    </test>    <test name = "Test On Edge">        <parameter name = "BrowserType" value="Edge"/>        <classes>            <class name = "tutorials.testproject.CrossBrowserTesting"/>        </classes>    </test>    <test name = "Test On Firefox">        <parameter name = "BrowserType" value="Firefox"/>        <classes>            <class name = "tutorials.testproject.CrossBrowserTesting"/>        </classes>    </test></suite>
package tutorials.testproject;import io.github.bonigarcia.wdm.WebDriverManager;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.edge.EdgeDriver;import org.openqa.selenium.firefox.FirefoxDriver;import org.testng.annotations.Parameters;import org.testng.annotations.Test;public class CrossBrowserTesting {  WebDriver driver;  @Test  @Parameters ( {"BrowserType"} )  public void testExamplePageOnMultipleBrowsers (String browserType) {    if (browserType.equalsIgnoreCase("Chrome")) {      WebDriverManager.chromedriver().setup();      driver = new ChromeDriver();    }    else if (browserType.equalsIgnoreCase("Edge")) {      WebDriverManager.edgedriver().setup();      driver = new EdgeDriver();    }    else if (browserType.equalsIgnoreCase("Firefox")) {      WebDriverManager.firefoxdriver().setup();      driver = new FirefoxDriver();    }    driver.manage().window().maximize();    driver.get("https://example.testproject.io/web/index.html");    System.out.println(browserType + ": " + driver.getTitle());  }}

В XML-файле тег параметра расположен на уровне теста. У нас есть возможность разместить тег на уровне тестового набора, на уровне теста или на обоих уровнях. Обратите внимание, что тег параметра имеет имя и значение с данными между двойными кавычками. Его имя, т.е. "BrowserType", передается тестовому сценарию через аннотацию @Parameters, а значение, т.е. "Chrome", передается в операторы if и else if.

Операторы if и else if устанавливают Chrome, Edge или Firefox. Каждый браузер получал команды от одного и того же тестового сценария после выполнения из XML-файла. Следующие результаты тестирования показывают, как успешно загружается страница TestProject, а консоль печатает уникальное имя браузера и заголовок страницы.

Кросс-браузерное тестирование в Selenium с помощью TestProject

OpenSDK / Закодированный тест

Существует 2 способа проведения кросс-браузерного тестирования с помощью TestProject. Мы можем использовать OpenSDK с открытым исходным кодом или AI-Powered Test Recorder. OpenSDK оборачивается в Selenium и поддерживает Java, C# или Python. Наши тестовые сценарии похожи на кросс-браузерное тестирование в Selenium с минимальными изменениями в коде и зависимостях. Мы должны включить зависимость TestProject для Maven или Gradle, импортировать драйверы браузера и передать токен.

<dependency>     <groupId>io.testproject</groupId>     <artifactId>java-sdk</artifactId>     <version>0.65.0-RELEASE</version> </dependency>
implementation 'io.testproject:java-sdk:0.65.0-RELEASE'
import io.testproject.sdk.drivers.web.ChromeDriver;import io.testproject.sdk.drivers.web.FirefoxDriver;import io.testproject.sdk.drivers.web.EdgeDriver;

AI-Powered Test Recorder

С помощью AI-Powered Test Recorder мы создаем новое веб-задание, затем выбираем несколько браузеров, таких как Chrome, Edge и Firefox. Тест в задании TestProject позволяет нам выбрать дополнительный источник данных CSV, если мы хотим выполнить тестирование на основе данных. Вот несколько скриншотов, показывающих шаги по выполнению кросс-браузерного тестирования и отчета.

Вот пошаговая демонстрация кросс-браузерного тестирования с помощью TestProject AI-Powered Test Recorder.

Выводы

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

Кросс-браузерное тестирование осуществляется с помощью Selenium и TestProject.

TestProject позволяет нам создавать собственные тестовые сценарии с использованием Java, C# или Python после добавления OpenSDK с открытым исходным кодом. OpenSDK является оберткой Selenium, поэтому он содержит команды Selenium плюс дополнительные команды из TestProject. Кроме того, мы можем использовать TestProject's AI-Powered Test Recorder для проведения кросс-браузерного тестирования. Это удобный процесс, который требует от нас только выбора браузера, который мы хотим использовать для кросс-браузерного тестирования.


Перевод статьи подготовлен в рамках курса "Java QA Engineer. Basic". Всех желающих приглашаем на двухдневный онлайн-интенсив Теория тестирования и практика в системах TestIT и Jira. На интенсиве мы узнаем, что такое тестирование и откуда оно появилось, кто такой тестировщик и что он делает. Изучим модели разработки ПО, жизненный цикл тестирования, чек листы и тест-кейсы, а также дефекты. На втором занятии познакомимся с одним из главных трекеров задач и дефектов Jira, а также попрактикуемся в TestIT отечественной разработке для решения задач по тестированию и обеспечению качества ПО.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru