Удаленка для jenkins+selenide+selenoid+allure+docker спецов на 2-3 часа в день. 100% remote! Присоединиться к проекту

SWD.Starter: Быстрый старт автоматизации тестирования UI на C# + Selenium WebDriver + PageObjects

csharp
page-factory
page-object
webdriver
Теги: #<Tag:0x00007fedb807f938> #<Tag:0x00007fedb807f7a8> #<Tag:0x00007fedb807f5a0> #<Tag:0x00007fedb807f460>

(Дмитрий Жарий) #1

В общем, история такая:
На основе некоторых своих (и не только своих) заметок в базе знаний по C# + WebDriver, я решил создать стартовый набор для фреймворка автоматизации тестирования SWD.Starter

Теперь, публикую про это все статью на Хабре:
SWD.Starter: Быстрый старт автоматизации тестирования UI на C# + Selenium WebDriver + PageObjects

Отвечу на любые вопросы по теме, как тут, на форуме AT.info, так и на Хабре.


Автоматизация на C# с нуля, фреймворки, альтернативы
Atata - свежий C#/.NET фреймворк на WebDriver
Изучение паттернов для автоматизации тестирования
Failed to start up socket within 45000 milliseconds. Windows Server 2012
Какой webdriver framework выбрать, если само приложение singlepage сайт на php + backbone, но есть только знания C#
FirePath Update (JS + XUL): как сохранять локаторы вместе с именами по указанному пути
(serg20) #2

Двитрий, спасибо огромное за ващу работу.
Есть вопрос по поводу смоук теста. Мне очень понравилась эта идея, но у меня большая часть страниц открываеться в процессе флоу и нет возможности делать смоук тест простым вызовом через УРЛ. Как мне в этом случае реализовывать Invoke?


(Дмитрий Жарий) #3

Привет @serg20,

Примеры кода есть тут:

http://blog.zhariy.com/2014/02/swd-page-recorder-seleniumcamp-2014.html
(Второе видео про Twitter)

dzharii/p-secamp-2014 -> PageDeclarations

  1. TwitterLoginPage.cs
  2. TwitterMainPage.cs
  3. TwitterAccountPage.cs

Если коротко, то алгоритм такой:

Когда вызывается TwitterAccountPage.Invoke() (это 3-я страница по глубине вложенности) он смотрит ли эта страница ещё не открыта и если не открыта, то вызывает:
TwitterMainPage.Invoke() (это 2-я страница по глубине вложенности).
После чего, TwitterAccountPage совершает на предыдущей странице некоторые действия для перехода от TwitterMainPage к TwitterAccountPage.

Алгоритм:

ТекущаяСтраница.Invoke() {
    Если  ТекущаяСтраница.IsDisaplyed() == false {
        ПредыдущаяСтраница.Invoke()
        ПредыдущаяСтраница.ВыполнитьДействияДляПереходаНаТекущуюСтраницу()
    }
}

(serg20) #4

Спасибо за быстрый ответ
Буду пробовать


(serg20) #5

Спасибо еще раз - все получается.
Я хочу еще в смок тест добавить снятие скринщота для последуюшего его сравнения с эталонным экземпляром. Где то помоему даже у вас был пример. Помниться там еще проблемы были с черными полями в интернет эксплорере…
Если есть какой то линк буду благодарен. Да и вообще хотелось бы посмотреть как правильно это делать - а то жалко такой красивый проэкт своим кодом портить :smile:


(Дмитрий Жарий) #6

Супер, @serg20,

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

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

В недавних изменениях, я немного упростил метод PageTest в файле Smoke_test_for_each_pageobject.cs:

Текущая версия выглядит так:

        public void PageTest(MyPageBase page)
        {
            // Implement Dispose inside page object in order to do cleanup
            using (page)
            {
                // SwdBrowser.HandleJavaScriptErrors();
                
                page.Invoke();

                // SwdBrowser.HandleJavaScriptErrors();
                
                page.VerifyExpectedElementsAreDisplayed();
                
                // SwdBrowser.HandleJavaScriptErrors();
            }
        }

Со скриншотом есть несколько вариантов. Первый – это просто добавить код сравнения в метод PageTest до или после строки

page.VerifyExpectedElementsAreDisplayed();

После чего (когда убедитесь что работает), этот код из PageTest можно будет вынести в отдельный метод. А имя реального PageObject класса (для сохранения скриншота), можно узнать, вызвав page.GetType()

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

На счёт чёрных экранов… я пытался запустить «безголовый» IE и в итоге у меня это нормально не получилось. В общем, IE должен запускаться как обычный браузер и показываться на экране.

Вроде бы раньше, была проблема со скриншотами для RemoteWebDriver. Для ее решения нужно использовать метод-расширение .TakeScreenshot() из OpenQA.Selenium.Support.Extensions

SwdBrowser.TakeScreenshot() как раз использует этот метод внутри.


(serg20) #7

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


(Дмитрий Жарий) #8

Вероятно, тема еще не раскрыта. В своих проектах я обычно создаю Wraper-класс Log, с методами Say, Error, Debug. Но, только реализация тут бывает очень разная.

Например, в SWD Page Recorder используется Nlog – очень удобная библиотека для логирования работы приложения. В тестах – я обычно использую в итоге Console.WriteLine “внизу”, а затем формирую отчет на основе xml файла прогона тестовых результатов.

Мне нравится подход с AOP, когда каждый метод автоматически может залогировать свой вызов,
http://blog.zhariy.com/2013/02/c-castle-dynamicproxy-humanizer.html

В Starter уже есть необходимый код, но он ещё нигде не прикручен и не докуменирован:
SWD.Starter / src / SWD.Core / Reporting /

Возможно, все таки допилю его.

Specflow и BDDfy также предоставляют свои механизмы логирования и отчётности. Их можно использовать в Starter.


(serg20) #9

Дмитрий, где можно определить свойства конкретного браузера?


(Дмитрий Жарий) #10

В WebDriverRunner.cs есть два метода, порождающие новый браузер.

Для RemoteDriver:
ConnetctToRemoteWebDriver – там уже есть case с DesiredCapabilities. Просто на следующей строке, в caps нужно добавить что вас интересует.

Для локального драйвера:

       private static IWebDriver StartEmbededWebDriver(string browserName)
        {
            switch (browserName)
            {

                case browser_Firefox:
                    return new FirefoxDriver();
                case browser_Chrome:
                    return new ChromeDriver();
                case browser_InternetExplorer:
                    return new InternetExplorerDriver();
                case browser_PhantomJS:
                    return new PhantomJSDriver();
                case browser_Safari:
                    return new SafariDriver();
                default:
                    throw new ArgumentException(String.Format(@"<{0}> was not recognized as supported browser. This parameter is case sensitive", browserName),
                                                "WebDriverOptions.BrowserName");
            }
        }

тут уже нужно добавить специфичные Capabilities в интересующих вас кейсах и передать их в конструктор драйвера.


(serg20) #11

Спасибо, буду пробовать. Собственно вопрос был вызван просмотром следующего видео - https://www.youtube.com/watch?v=0_kAPWSZNY4
Используете вы эти возможности или что то другое для проверки перформенс внутри ваших тестов. примеры кода преветствуются :slight_smile:


(Дмитрий Жарий) #12

Неа, никогда автотестами перформанс страницы не измерял.
Для меня хватало логов Fiddler’а и профайлера для SQL Server / Oracle и самописного парсера для того чтобы определить самые частые запросы к веб серверу и к базе данных.

За 3 года работы я столкнулся лишь раз с проблемой утечки памяти, которая влияла на перформанс приложения (Single Page Application, т. е. там клиентского JavaScript было больше чем серверного кода).

Так что, помочь не могу с этим вопросом, но если кто другой эту тему увидит, то уточните пожалуйста, какой именно перформанс вас интересует:

  1. Клиентский JavaScript / отзывчивость пользовательского интерфейса
  2. HTTP запросы на сервер и отклик от сервера
  3. Скорость загрузки веб страницы и ее компонентов

или что-то ещё?
По пунктам 2 и 3 можно использовать BrowserMobProxy или FiddlerCore.
По 1-му пункту, я думаю можно использовать WebDriver, но только с Capability nativeEvents=true

Вот еще интересное видео про JavaScript перформанс и бенчмарки, но оно больше для разработчиков, чем для тестировщиков:


(serg20) #13

Интересует в основном пункт 1 то есть перформенс пользовательского интерфейса. Остальное я могу видеть на своих запусках лоад тестов. Но если вдруг у меня страница начала грузиться у клиента в 2 раза дольше то у меня фактически нет никакой индикации, кроме слов пользователя.


(Дмитрий Жарий) #14

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

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

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

В этом примере Starter, есть метод WaitForOpen()

        internal void WaitForOpen()
        {
            lnkDeactivateMyAccount.WaitUntilVisible(TimeSpan.FromSeconds(10));
        }

Его можно легко параметризировать, добавив параметр таймаута:

        internal void WaitForOpen(int timeOutSeconds)
        {
            lnkDeactivateMyAccount.WaitUntilVisible(TimeSpan.FromSeconds(timeOutSeconds));
        }

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

А может быть лучше в этом методе, в цикле while дергать isDisaplyed пока не вернётся true или не будет привешено время ожидания общем… это как идея.

Получение более детальной информации о рендеринге – это более сложное занятие.

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

В зависимости от выбранного JavaScript фреймворка, можно получить какую-то часть информации. Вот пример EmberJS:
How to instrument and measure page rendering time?

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

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

Ссылки по теме:

Ну, и… хочу поделится видео, которое тестирует «отзывчивость» интерфейса Todo MVC для разных реализаций.

Это видео показывает, что реализации BackboneJS и KnockoutJS работают примерно в два раза быстрее чем AngularJS при добавлении новых элементов… Какие выводы можно тут сделать?
Ну… не знаю пока :smiley:


(serg20) #15

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


(Дмитрий Жарий) #16

Удачи! Да, обязательно отпишитесь о своем опыте. Решение такой задачи – не самая травильная работа.


(Михайленко Владимир) #17

Дмитрий, спасибо вам за ваш фреймворк и рекордер.
Начал (<2мес - это и опыт ОО программирования) автоматизировать тесты (смоук+функционал) используя Selenium + Protractor .NET (Page Object). Вопрос такой, на сколько реально подружить ваш фреймворк с Protractor ом?
Или возможно причесывать свой слюшин исходя из вашего кода/структуры?


(Дмитрий Жарий) #18

Привет @mihaylenkov

На первый взгляд, добавление Protractor for .NET не должно составить проблем.

Насколько я понял, NgWebDriver – это обвертка над обычным WebDriver, т.е. вначале вам нужно создать обычный объект WebDriver, а затем обвернуть его в NgWebDriver. типа, как в примере:

NgWebDriver ngDriver = new NgWebDriver(driver);

С этим не должно возникнуть проблем.
В неймспейсе Swd.Core.WebDriver
есть класс WebDriverRunner у которого есть метод Run, который создает новый объект WebDriver. Можете сделать обвертку там и вернуть экземпляр NgWebDriver.

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

С другой стороны, я думаю, вы вполне можете обойтись, XPath или CSS локаторами, без использования отдельного фреймворка. Просто в случае с Angular, уже более важными становятся эти ng-* атрибуты.

Я так же записал видео, которое получилось не очень веселым и удачным, но иллюстрирует работу с Angular, используя XPath и CSS (первые 6 минут)

Удачи


(Михайленко Владимир) #19

Дима, спасибо за быстрый ответ.
Да, согласен, пока XPath везде работает как нужно, пока использую By.
Я так подозреваю, что возникнет вопрос с ожиданиям пока Ангуляр отрендерит и плучит все ответы от сервера. Если я не ошибаюсь, то в Protractor используется WaitForAngular. ( https://github.com/bbaia/protractor-net/search?utf8=✓&q=WaitForAngular ).
На правильном ли я пути.


(Дмитрий Жарий) #20

Вроде бы на правильном :wink:
Единственное, чтобы я порекомендовал, это в случае использования Protractor, замерить время прохода тестов с Protractor и без него.

По сути, этот WaitForAngular выполняется при каждом действии с элементом, а это удваивает количество вызовов к WebDriver. Это не так критично при локальном запуске (браузер и код на локальной машине), но может ощутимо сказаться на времени работы через RemoteDriver (браузер и код на разных машинах).

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