Показаны сообщения с ярлыком Watin. Показать все сообщения
Показаны сообщения с ярлыком Watin. Показать все сообщения

воскресенье, 3 марта 2013 г.

Watin - как написать робота?

Начнем с основ - что вообще такое робот и зачем он нужен?

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

Зачем она нужна? Затем, что автоматизировать можно не всякий проект. Хотя бы потому, что у вас, начинающего автоматизатора, просто нет нужных знаний и опыта. А тестировать нужно здесь и сейчас. А проект у вас всего на пару-тройку месяцев. И помимо него еще целая куча разных дел.

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

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

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

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

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

Watin - Web Application Testing in .Net. Последователь Watir - Web Application Testing in Ruby, созданный для того, чтобы писать простые и понятные, легко-читабельные тесты на языке .Net. Имеющий свои недостатки, но, тем не менее, отлично подходящий для такой работы, как быстрая помощь себе в написании робота.

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

Чтобы тестировать движение по статусам, нам надо заполнять эту форму, создавая, таким образом, в своей системе новую карточку. Допустим также, что наше приложение пишется на C# и у нас уже есть Microsoft Visual Studio, в которой работают наши разработчики.

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

Итак, открываем Microsoft Visual Studio и начинаем делать робота... (в полной версии статьи можно не читать вступление, оно точно такое же, просто я хотела сохранить эту ссылку в блоге)

Важно!

В конце статьи есть ссылка на скачивание самого робота - можно скачать, добавить туда библиотечки Watin и посмотреть, как все работает, не написав ни единой строчки кода !!!

А также можно сохранить себе этот проект как пример и на его основе написать собственного робота - ведь переделать существующее всегда проще, чем написать с нуля. Удачи! Smile :)

среда, 28 декабря 2011 г.

Самописный робот на Watin

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

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

Если мы хотим видеть на выхлопе нормальные данные, а не свои пьяные танцы, то можем использовать копипасту. Однако радости мало каждое поле копировать - вставлять.

Еще варианты? Правильно! Роботы!
Один из самых известных - imacros для мозиллы. Простой, удобный и тд.

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

Тогда на помощь нам приходят... самописные роботы!

Об одном мы с вами и поговорим. Microsoft Visual Studio + Watin преставляют...

Итак. что мы хотим? Хотим заполнить форму. Или две формы. Одинаковые по сути, но разные по смыслу. Ну, например, представим, что у нас везде, на всех сайтах, окно регистрации одно и тоже. И вот мы хотим регистрироваться в Гугле и Яндексе. А формы - одни и те же, поля теже самые. Ну вот захотелось им так.

Что делаем мы? Открываем Microsoft Visual Studio и создаем "Windows Forms Application"
Получаем нашу формочку:



Далее выбираем View - Toolbox
В открывшемся окошке раздриливаем All Windows Forms  и выбираем кнопку:




Перетаскиваем кнопку на нашу формочку.
Справа внизу видим ее свойства, Properties - вбиваем там в поле Text значение "Регистрируемся в Google". Потом добавляем вторую, которая будет - "Регистрируемся в Yandex".

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

Теперь вступает в силу Watin.

Создаем отдельный проект - Class Library, в нем создаем класс "Model"
В модели мы показываем, с кем и как мы имеем дело:
        private WatiN.Core.Browser browser;

        public Model(Browser browser)
        {
            this.browser = browser;
        }

        public void РегистрируемсяВGoogle()
        {
            browser.GoTo(@"http://adress");
        }

        public void ЗадатьФамилию (string lastName)
        {
            browser.TextField(Find.ById("txtLastName")).TypeText(lastName);
        }

        public void ЗадатьИмя (string firstName)
        {
            browser.TextField(Find.ById("txtFirstName")).TypeText(firstName);
        }


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

Находим их через FireBug, получаем id, class и тд. По ним и ищем. Просто и быстро)

Вбивать параметры мы можем как? Можем уже в тестах. Можем добавить файл в проект (уже в наш "Windows Forms Application", а не "Class Library").

Если делаем файлом: обавляем в проект файл типа XML File.
Заполняем его своими разными значениями.

<ФормыВвода>
 <Форма lastName="Киселева" firstName="Ольга"
         />
<Форма lastName="Абаев" firstName="Николай"
         />
</ФормыВвода>


Что мы ему сказали? Что у нас есть два вариант заполнения формы - Киселева Ольга и Абаев Николай. Все параметры записываем через пробел, пихаем сколько влезет, как параметров, так и вариантов.

Теперь вернемся к коду робота. Который открывается аблкликом по кнопке на нашей форме.

Что мы пишем в нем?

    public partial class Робот : Form
    {
        public Робот()
        {
            InitializeComponent();
        }

        private void btnHellow_Click(object sender, EventArgs e)
        {
            //открыли браузер
            var rnd = new Random();
            var browser = new IE();
            browser.BringToFront();

            //перешли на нужную форму, прочитав по пути  XML
            var моделька = new Model(browser);
            моделька. РегистрируемсяВGoogle ();
            моделька.ОткрытьФормуРегистрации();
            XmlDocument doc = new XmlDocument();
            doc.Load(Path.GetFullPath("Data.Xml"));

            //Получаем рандомные значения из XML
            var forms = doc.DocumentElement.ChildNodes;
            var randomIndex = rnd.Next(forms.Count);
            var form = forms[randomIndex];

            //Присваиваем переменным значения из XML
            string lastName = form.Attributes["lastName"].Value;
            string firstName = form.Attributes["firstName"].Value;
            bool alert = bool.Parse(form.Attributes["alert"].Value);

            //Заполняем формочку
            моделька.ЗадатьФамилию (lastName);
            моделька.ЗадатьИмя (firstName);          

            моделька.ПодтвердитьРегистрацию();
        }

Я туда помимо Фамилии и Имени добавила значение чек-бокс, в котором стоит галка, или не стоит. В XML мы этому параметру присваиваем значение true\false.

Таким образом, что мы имеем? Мы имеем по нажатию на кнопочку заполнение формы любым из приведенных вариантов.

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

Также это приложение теперь можно сохранить для тех коллег, у кого нет Студии - включаем режим релиза:



Компилируем наш проект.
(Если хотим исходный код отправить - полезно сделать так: Build - Clean Solution. Так он меньше места занимать будет)

Потом открываем папку с нашим приложением\bin\Release
Удаляем все лишние файлы - с расширениями pdb и xml (кроме того, который используем в проекте).

Потому всю папку Release упаковываем и отсылаем по почте :)

четверг, 10 ноября 2011 г.

LogonDialogHandler VS Windows 7 (Решение в Watin)

Заметки юного автоматизатора... Так уж получилось, что свои тесты я синхронизирую между тремя компами - рабочий ноутбук (ХР), домашний ноутбук (Vista) и домашний комп (Win7). Компьютер у меня очень "вовремя" полетел, так что поставила студию и на домашний ноут. Проблем с двумя ноутбуками не было никаких - все хорошо, все круто, все работает. А потом мне вернули комп... У вас тоже Windows 7? И вы хотите начать автоматизацию? Тогда честно вам скажу, семерка - зло!

Нет. Не так.

Windows 7 - ЗЛО!!! Особенно для начинающих автоматизаторов, которые в код то круглыми глазами смотрят, "ой, а что это?", а тут еще идеально работающий код валится на первой же стадии.

Да да, это страшное слово "Залогиниться". При моих попытках освоить TestComlete я, кстати, семерку так и не победила. Неуверенно тыкаясь в кнопочку рекорда и осторожно корректируя полученный скрипт до понятного, я была страшно огорчена тем, что прекрасно работающие тесты на ХР не запускаются дома. Вообще. Ведь для любого теста надо вначале залогиниться... А он не видит найденный ранее Alias. Пыталась перезаписать скрипт, найти окно логина семерки. Но проиграла этот раунд)))

Потом у меня кончилась триал-версия, так что больше я TestComlete не занималась, и как обойти окно логина там, я не знаю.

Едем дальше. Watin, VS 2010, C#. Все хорошо, все здорово. Запись логина:

browser.DialogWatcher.Add(new LogonDialogHandler(@"login", "password"));

На семерке запускаю - вводит логин и пароль в строку логина, причем упорно так вводит, раз 20 ввел, пока скрипт не остановила. Пошла за помощью к программистам. Посидели, погуглили. Нашли обход.

Делаем так. В панели инструментов у нас есть такая штучка, как Debug, если нет, вытаскиваем ее туда:




Открываем выпадающий список - Configuration Manager. А теперь угадайте, где в этом менеждере кнопка "добавить новую конфигурацию" )))))

Там снова открываем выпадающий список "Active solution configuration " и находим завестную кнопочку "new". Создаем себе "WIN7 Debug"

Потом открываем свойства проекта - закладку Build и вводим в поле Conditional compilation symbols "WIN7"

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

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

#if
WIN7 Browser.DialogWatcher.Add(new Windows7LogonDialogHandler(@"login", "password"));
#else
Browser.DialogWatcher.Add(new LogonDialogHandler(@"login", "password"));
#endif

Но и это еще не все! Создаем новый класс, пишем туда большой и страшный код, который я приведу внизу, а то не вижу тут тега спойлера. Класс называем "Windows7LogonDialogHandler .cs"
К нему то наша строчка и будет обращаться. Далее все просто. Если запускаем с ХР или Висты - конфигурацию выбираем "Debug", если с семерки - "WIN7 Debug". И все работает!

Ага. Щазззз. Проблемы могут и не кончиться)) Итак, мы прогуглили это дело, нашли тот скрипт для класса "Windows7LogonDialogHandler .cs" , который будет ниже, запустили (у программиста моего тоже семерка на рабочем компе) - работает! Ура?

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

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

Подключаю товарищей, дебажим код и что выясняется? У коллеги англ семерка, а у меня - русская. Поменяла в коде три названия, если у вас англ семерка, вам надо будет сменить "Другая учетная запись", "Пользователь" и "Пароль" на англ наименования... А я надеюсь, что мне не придется запускать свои тесты на английской семерке )))))

Хотя надо что-то придумать с этим. Нехорошо. Можно просто еще одну конфигурацию, в принципе, сделать... Надо ли? Ладно, посмотрим. Надеюсь, что когда вы начнете изучать автоматизацию, то проблему логина вы быстро обойдете, уже зная решение :))  А так в принципе это пока единственная проблема семерки, которую я знаю. Дальше все пойдет хорошо, главное, залогиниться!

Собственно, скрипт для русской версии:

using System.Linq;
using System.Windows.Automation;
using WatiN.Core.DialogHandlers;
using WatiN.Core.Native.Windows;


namespace UIModel
{

public class Windows7LogonDialogHandler : BaseDialogHandler{
{

private readonly string _username;
private readonly string _password;
AndCondition _conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
readonly AndCondition _listCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));
readonly AndCondition _editCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
readonly AndCondition _buttonConditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));

public Windows7LogonDialogHandler(string username, string password)
{
_username = username;
_password = password;
}
public override bool HandleDialog(Window window)
{
if (CanHandleDialog(window))
{
var win = AutomationElement.FromHandle(window.Hwnd);
var lists = win.FindAll(TreeScope.Children, _listCondition);
var buttons = win.FindAll(TreeScope.Children, _buttonConditions);
var another = (from AutomationElement list in lists
where list.Current.ClassName == "UserTile"
where list.Current.Name == "Другая учетная запись"
select list).First(); another.SetFocus();
foreach (var edit in from AutomationElement list in lists
where list.Current.ClassName == "UserTile"
select list.FindAll(TreeScope.Children, _editCondition)
into edits
from AutomationElement edit in edits
select edit)

{
if (edit.Current.Name.Contains("Пользователь"))
{
edit.SetFocus();

var usernamePattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (usernamePattern != null) usernamePattern.SetValue(_username);
}


if (edit.Current.Name.Contains("Пароль"))
{
edit.SetFocus();
var passwordPattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (passwordPattern != null) passwordPattern.SetValue(_password);
}
}

foreach (var submitPattern in from AutomationElement button in buttons
where button.Current.AutomationId == "SubmitButton"

select button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern)
{
submitPattern.Invoke();
break;
}

return true;
}
return false;
}

public override bool CanHandleDialog(Window window)
{
return window.ClassName == "#32770";
}
}
}