Статья расскажет о том, как настроить фреймворк автоматизированного тестирования пользовательского интерфейса на языке C#, вместе с Selenium WebDriver и паттерном PageObjects.
Стартовый набор с открытым исходным кодом – SWD.Starter – поможет написать и запустить ваш первый тест в течении 10 минут. Кроме этого, предлагая архитектуру фреймворка, основанную на хороших практиках автоматизации тестирования.
Весь код SWD.Starter может быть полностью настроен под ваши задачи.
Что такое SWD.Starter?
SWD.Starter – это стартовый набор для вашего фреймворка автоматизации тестирования. Весь исходный код доступен на GitHub: dzhariy/SWD.Starter, а лицензия проекта (unlicense), позволяет вам использовать исходный код как угодно, хоть продавать.
SWD.Starter – это уже настроенный проект, содержащий весь необходимый инфраструктурный код для начала создания и запуска тестов пользовательского интерфейса через Selenium WebDriver.
SWD.Starter настойчиво рекомендует использование паттерна PageObjects. И в случае использования этого паттерна, вы сможете писать новый код авто-тестов действительно быстро, при этом, сохраняя красивую архитектуру и читабельность кода.
Что необходимо для начала
Для запуска проекта, вам будет необходимо следующее программное обеспечение:
- Visual Studio Express 2013 Desktop Edition (также, теоретически, поддерживаются VS2010 и VS2012)
- Git для выкачки проекта из Github
- Дополнительные драйвера браузеров Selenium WebDriver, которые можно скачать с официальной страницы проекта
Для быстрой и удобной установки ПО, я рекомендую использовать пакетный менеджер для Windows – Chocolatey.
Согласно инструкциям на главной странице, откройте cmd.exe, и в консольном окне, просто выполните следующий код:
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin
А далее, в том же консольном окне, выполните следующие команды:
- cinst VisualStudioExpress2013WindowsDesktop
- cinst git
Теперь, в консольном окне (например в cmd.exe или Far Manager), выберете папку, куда вы хотите клонировать SWD.Starter – и запустите команду:
git clone https://github.com/dzhariy/SWD.Starter.git
Это обязательный шаг, иначе проект не скомпилируется:
Скопируйте chromedriver.exe и IEDriverServer.exe в папку SWD.Starter\webdrivers
А вот видео полной установки на чистую виртуалку (которую я скачал с modern.ie )
На всякий случай замечу, на видео видно, что Windows на виртуальной машине требует активацию.
Cогласно лицензионным условиям modern.ie я имею право использовать такие образы легально в тестовых целях. В пользовательском соглашении сказано, что я не должен активировать Windows в этом случае.
Что такое PageObjects и почему это настолько важно?
Если говорить просто, то подход в автоматизированном тестировании, с использованием PageObjects, заключается в том, что вы просто выносите весь код низкоуровневой работы со страницей (например, набор текста и нажатия мышкой по элементам) в отдельные классы.
Теперь ваши тесты не работают со страницей напрямую, вызывая низкоуровневые методы WebDriver, а используют более высокоуровневые операции, специфичные для каждой страницы.
Это сокращает количество строк кода в тестах, тем самым делая код более читабельным, понятным и надёжным.
Подход PageObjects – это альтернатива бот-стилю – вызову методов WebDriver из тестов напрямую.
В самом начале, бот-стиль кажется проще и понятнее чем использование PageObjects. Но, это огромное заблуждение, которое может привести ваш проект автоматизации к краху.
Со временем, когда количество тестов будет расти, в случае использования бот-стиля, вы будете тратить все больше и больше времени на их поддержку. В итоге, поддержка фреймворка автоматизации будет экономически не выгодной и руководством проекта будет принято решение вернутся к ручному тестированию. А уже написанный код останется только выбросить, по причине того, что он уже не соответствует реальному тестируемому приложению.
Тесты в бот-стиле подобны огромной не отсортированной куче книг. Когда ваша «куча» состоит всего из 10-ти книг, в ней можно разобраться без особых трудностей.
Но, что вас ждёт, когда количество книг возрастёт до 100? Поверьте, я вам не завидую. Просто потому, что сам через это уже прошёл.
С другой стороны, при использовании PageObjects, можно разложить все книги по полочкам. В книжных магазинах и на складах, содержится огромное количество книг. Тем не менее, продавцы могут быстро найти то, что вам нужно.
PageObject-класс – это книжная полка, позволяющая удобно организовать код работы с веб-страницей. А популярные языки программирования и IDE предоставляют значительно больше возможностей при использовании объектно-ориентированного программирования.
Тесты в бот-стиле
Основное преимущество тестов в бот-стиле то, что вы можете их «записать даже не зная языка программирования», при помощи таких инструментов, как Selenium IDE и Selenium Builder.
В результате, может получится нечто такое:
class BrittleTest { [Test] public void Can_buy_an_Album_when_registered() { var driver = Host.Instance.Application.Browser; driver.Navigate().GoToUrl(driver.Url); driver.FindElement(By.LinkText("Admin")).Click(); driver.FindElement(By.LinkText("Register")).Click(); driver.FindElement(By.Id("UserName")).Clear(); driver.FindElement(By.Id("UserName")).SendKeys("HJSimpson"); driver.FindElement(By.Id("Password")).Clear(); driver.FindElement(By.Id("Password")).SendKeys("!2345Qwert"); driver.FindElement(By.Id("ConfirmPassword")).Clear(); driver.FindElement(By.Id("ConfirmPassword")).SendKeys("!2345Qwert"); driver.FindElement(By.CssSelector("input[type=\"submit\"]")).Click(); driver.FindElement(By.LinkText("Disco")).Click(); driver.FindElement(By.CssSelector("img[alt=\"Le Freak\"]")).Click(); driver.FindElement(By.LinkText("Add to cart")).Click(); driver.FindElement(By.LinkText("Checkout >>")).Click(); driver.FindElement(By.Id("FirstName")).Clear(); driver.FindElement(By.Id("FirstName")).SendKeys("Homer"); driver.FindElement(By.Id("LastName")).Clear(); driver.FindElement(By.Id("LastName")).SendKeys("Simpson"); driver.FindElement(By.Id("Address")).Clear(); driver.FindElement(By.Id("Address")).SendKeys("742 Evergreen Terrace"); driver.FindElement(By.Id("City")).Clear(); driver.FindElement(By.Id("City")).SendKeys("Springfield"); driver.FindElement(By.Id("State")).Clear(); driver.FindElement(By.Id("State")).SendKeys("Kentucky"); driver.FindElement(By.Id("PostalCode")).Clear(); driver.FindElement(By.Id("PostalCode")).SendKeys("123456"); driver.FindElement(By.Id("Country")).Clear(); driver.FindElement(By.Id("Country")).SendKeys("United States"); driver.FindElement(By.Id("Phone")).Clear(); driver.FindElement(By.Id("Phone")).SendKeys("2341231241"); driver.FindElement(By.Id("Email")).Clear(); driver.FindElement(By.Id("Email")).SendKeys("chunkylover53@aol.com<script type="text/javascript"> /* <![CDATA[ */ (function(){try{var s,a,i,j,r,c,l,b=document.getElementsByTagName("script");l=b[b.length-1].previousSibling;a=l.getAttribute('data-cfemail');if(a){s='';r=parseInt(a.substr(0,2),16);for(j=2;a.length-j;j+=2){c=parseInt(a.substr(j,2),16)^r;s+=String.fromCharCode(c);}s=document.createTextNode(s);l.parentNode.replaceChild(s,l);}}catch(e){}})(); /* ]]> */ </script>"); driver.FindElement(By.Id("PromoCode")).Clear(); driver.FindElement(By.Id("PromoCode")).SendKeys("FREE"); driver.FindElement(By.CssSelector("input[type=\"submit\"]")).Click(); Assert.IsTrue(driver.PageSource.Contains("Checkout Complete")); } }
Такой подход может быть оправдан при выполнении одноразовых задач. Например, если вам необходимо создать 1000 пользователей через интерфейс приложения – достаточно записать создание одного, и с минимальными изменениями поместить код в цикл.
Такой подход будет губительным, если вы надеетесь на автоматизацию тестирования в долгосрочной перспективе.
Вот один небольшой пример:
Предположим, доступ к одной странице продукта осуществляется из 30-ти тестов. В один прекрасный день, программисты принимают решение поменять вёрстку страницы:
Теперь и элементы называются по другому, и «логика кликов» меняется.
В этом случае, вам будет необходимо внести изменение в 30 тестов, вместо того, чтобы сделать это в одном классе.
Как вы думаете, сколько времени эта интереснейшая работа займёт?
Тесты с использованием PageObject-классов
Если просто вынести куски кода и организовать все виде нескольких PageObject-классов, то с кодом теста происходят чудесные превращения: он стает понятным, появляются действия, которые можно переиспользовать в других тестах, вместо того чтобы копи-пастить вызовы WebDriver.
Обратите внимание, что в коде теста стало больше строк… Но, это ведь только за счёт комментариев и пояснений, которые важны для демонстрации в этой статье, но необязательны в вашем реальном коде.
Уберите все комментарии и пустые строки – и код все равно останется читабельным и сократится по количеству строк.
class PageObjectTest { [Test] public void Can_buy_an_Album_when_registered() { // Обычно, конструкторы PageObject объектов не выполняют действий на странице. // Они необходимы лишь для получения ссылки на объект. var registerUserPage = new RegisterUserPage(); // Просто открывает страницу регистрации, при этом, // кликая на все нужные ссылки по пути registerUserPage.Invoke(); // Этот класс используется для передачи данных. // Некоторые данные могут быть заполнены «по умолчанию», но об этом – позже var newUserFromData = new UserFromDataData() { UserName = "HJSimpson", Password = "!2345Qwert", }; // Момент заполнения и отправки формы registerUserPage.FillForm(newUserFromData); registerUserPage.Submit(); // А следующий код выбирает товар из витрины, добавляет его в корзину // и переходит на страницу оформления заказа. var showCasePage = new ShowCasePage(); showCasePage.Goto("Disco"); showCasePage.SelectProduct("showCasePage"); showCasePage.AddToCard(); showCasePage.Checkout(); var checkOutForm = new CheckOutForm(); // .DefaultValues возвращает класс с уже заполненными данными по умолчанию. // Если нас что-то не устраивает – всегда можно заменить. var checkoutFromData = UserCheckoutFromData.DefaultValues; // Вот как раз это и не устраивает! А давайте JavaScript инъекцию добавим! checkoutFromData.Email = @"chunkylover53@aol.com<script type=""text/javascript""> /* <![CDATA[ */ (function(){try{var s,a,i,j,r,c,l,b=document.getElementsByTagName(""script"");l=b[b.length-1].previousSibling;a=l.getAttribute('data-cfemail');if(a){s='';r=parseInt(a.substr(0,2),16);for(j=2;a.length-j;j+=2){c=parseInt(a.substr(j,2),16)^r;s+=String.fromCharCode(c);}s=document.createTextNode(s);l.parentNode.replaceChild(s,l);}}catch(e){}})(); /* ]]> */ </script>"; CheckoutCompletePage checkoutCompletePage = checkOutForm.Submit(); Assert.IsTrue(checkoutCompletePage.GetPageTitle().Contains("Checkout Complete")); } }
Ну что? Хотите создавать тесты, используя PageObject?
Первый Smoke-тест в SWD.Starter
Если вы задаётесь вопросом: с чего начать автоматизацию тестирования? То, у меня для вас есть очень простой ответ, который подойдёт в 99% случаев.
Начните со смоук тестов для каждой страницы приложения.
Рецепт:
- Взять страницу любого уровня вложенности
- Открыть страницу
- Проверить, что все важные элементы – присутствуют на странице.
И в результате мы получим легковесный тест, который в случае успешного прохода говорит:
Что все важные элементы отдельной страницы до сих пор не изменились
То, что наши PageObject классы по прежнему соответствуют актуальной странице
То, что путь из точки А. (главная страница) в точку Б. (любая другая страница) – возможен для конечно пользователя приложения.
И все это работает в разных браузерах.
А теперь, давайте напишем первый тест для страницы регистрации нового пользователя Хабрахабр:
Покрыв все страницы приложения такими тестами – вы будете приятно удивлённы: метрики покрытия покажут покрытие больше 50%. Конечно же, мы понимаем, что метрика покрытия кода – не самая основная, но согласитесь, это – хороший результат.
Кроме того, в SwdBrowser.cs есть метод HandleJavaScriptErrors(). В данной реализации, его нужно просто почаще вызывать, например, в каждом .Invoke(). И тогда, этот метод сможет отловить возможные неожиданные ошибки JavaScript.
Я надеюсь, что в ходе просмотра видео, вы заметили несколько интересных вещей?
Например, что в проекте уже готова инфраструктура для смоук-тестов PageObject-классов?..
И чтобы добавить тест – необходимо просто его записать, сгенерировать код… и следовать инструкциям в сгенерированном коде.
А в самом начале, мы видим строку кода:
[TestMethod] public void S01_First_Step_Run_WebDriver_with_Firefox() { SwdBrowser.Driver.Url = "http://swd-tools.com"; }
которая: открывает браузер, переходит по нужному URL… и закрывает браузер.
Не много ли это для одной строки?
И почему открылся именно FireFox, а что если я хочу Internet Explorer?
Об этом и многом другом – ниже.
Хорошие практики в автоматизации тестирования
Вы знаете, опасно называть практики «лучшими», и поэтому, оставим просто «хорошими».
Время от времени, я описываю такие практики в виде небольших заметок, которые иллюстрируют конкретное решение, но, к сожалению, не показывают общей картины.
Для того, чтобы показать, как хорошие практики работают вместе, я и начал работу над SWD.Starter.
Вот, например, по статье Автоматическое создание Браузера и инициализация PageObject как раз и был реализован SwdBrowser. А PageObject классы, унаследованные от CorePage – умеют самостоятельно инициализировать веб-элементы.
А в заметке WebDriverWait и PageObject, я рассказываю, как добавить «умные» методы ожидания элементов для PageObject, по типу WebDriverWait для обычных элементов.
Все это уже вошло в SWD.Starter. И если вас интересует решение конкретной проблемы – просто посмотрите код, а я, со временем, сделаю так, чтобы в нем можно было легко разобраться. Уже сейчас, некоторые классы в достаточной мере документированы, например – Swd.Core.Configuration.Config Class. А комментарии для некоторых классов уже есть в коде, но пока ещё не мигрировали в Doxygen.
Структура проекта SWD.Starter
Ядро SWD.Starter – это Swd.Core. В нем содержатся такие интересные штуки как:
- Класс Swd.Core.Configuration → Config, который читает настройки фреймворка из внешнего файла Config.config. Именно в этом файле можно выбрать запускаемый браузер, а также добавить свои настройки.
- Класс Swd.Core.WebDriver → SwdBrowser – уже упоминался. Он управляет жизнью браузера. А рядом, в том же пространстве имен, находятся полезные методы и классы, упрощающие работу с браузером.
- В пространстве имен Swd.Core.Pages, живут базовые классы для PageObject’ов.
В Swd.Core расположен только общий код, который, в дальнейшем, можно расширить в дочерних проектах по тестированию.
Пример такого тестового проекта – DemoProject.
Тестовый проект состоит из двух основных подпроектов:
- Demo.TestModel – содержит декларации PageObject-классов, кастомизированные базовые классы, необходимые данные, кусочки логики работы приложения, и другие библиотечные функции, специфичные для конкретного тестируемого приложения.
Обычно, для отдельного тестируемого приложения должна быть лишь одна библиотека Модели. - Demo.TestProject – проект, содержащий наборы тестов. Таких тестовых проектов может быть несколько. Вот, например, Demo.Tutorial – это тоже проект с тестами, и он также как Demo.TestProject использует библиотеку Модели (Demo.TestModel ).
- Demo.Tutorial – попытка создания руководства по работе с Swd.Starter. Пока ещё не совсем законченный, но уже сейчас, можно читать файл «Ch00Introduction.cs» и пробовать запускать тесты.
Обратная связь, лицензия и сотрудничество
Лицензия проекта позволяет вам производить любые действия с кодом проекта, которые может ограничить лишь ваша фантазия. (http://unlicense.org/)
Код можно видоизменять, использовать в коммерческих целях, выкладывать на торренты и майнить биткоины, если хотите.
Но, мне бы было очень полезно получить от вас обратную связь. Оставить комментарии можно как тут, так и на странице проекта на Github.
А лучше всего, если вы отправите реальный чёткий пацанячий pull-request в репозиторий на github.
Но, если это будет огромное изменение с перелапачиванием половины кода, то, неплохо было бы вначале его обсудить.
Над чем можно работать? – Там поле почти не паханное:
- Документация
- Туториал
- Новый полезный код, решающий реальные проблемы
- Новые демонстрационные проекты
- Инструкции
И ещё. 28-го февраля 2014 в Киеве, я планирую выступать с докладом на конференции Selenium Camp 2014. Доклад будет посвящён проекту SWD Page Recorder, но и проекту SWD.Starter будет посвящено не мало времени. А после, запись доклада появится в разделе архива материалов, через 3-4 месяца после конференции.
Я буду доступен все два дня, и буду готов пообщаться «в живую» как после моего доклада, так и в течении всего времени конференции.
Полезные материалы
- SWD Tools – страница проектов Page Recorder и Starter. Содержит (или будет содержать) все необходимые ссылки, касательно проектов
- GitHub SWD Starter
- GitHub SWD Page Recorder
- SWD Page Recorder: Записывает PageObject-классы для Selenium WebDriver – Обзор SWD Page Recorder на Хабрахабр
- Вебинар: Основы использования паттерна Page Object вместе с Selenium WebDriver
- Подборка свежих заметок и статей по автоматизации тестирования – если вам интересны советы по автоматизации тестирования
- Слайды/Видео к моему докладу на #atdays: За пределами PageObject
- Maintainable Automated UI Tests – отличная статья, с современным взглядом на автоматизацию тестирования
Успешной вам автоматизации.
P.S.: SWD расшифровывается как Selenium WebDriver
ссылка на оригинал статьи http://habrahabr.ru/post/208822/
Добавить комментарий