Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

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

csharp
page-factory
page-object
webdriver
Теги: #<Tag:0x00007f7b697dea38> #<Tag:0x00007f7b697de6f0> #<Tag:0x00007f7b697de4c0> #<Tag:0x00007f7b697de308>

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

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

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

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


Автоматизация на C# с нуля, фреймворки, альтернативы
Изучение паттернов для автоматизации тестирования
Atata - свежий C#/.NET фреймворк на WebDriver
Какой webdriver framework выбрать, если само приложение singlepage сайт на php + backbone, но есть только знания C#
Failed to start up socket within 45000 milliseconds. Windows Server 2012
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 добавил в том случае, когда бы столкнулся с реальной проблемой, которую он решает, например, если тратится много усилий и кода для ожидания элементов.