.NET
June 2010 21

Time for Coded UI Tests

В данной статье я постараюсь детально рассказать о работе с Coded UI Test, одним из множества нововведений Visual Studio 2010, а так же упомянуть о проблемах, с которыми я столкнулся.

Не будем зря терять время и сразу приступим к тестированию.


У меня уже есть простое WPF приложение, интерфейс которого и будет подвергнут тестированию. Он представляет из себя форму с кнопкой, двумя TextBox'ами для ввода данных и еще одним — для вывода.
image

Создадим новый тестовый проект, используя Visual Studio 2010.
image

Добавим новый Coded UI тест. Сделать это можно либо через Solution Explorer, либо через пункт Test главного меню.
Нам предлагается ввести имя файла и выбрать, в какой тестовый проект его добавить.
image

После нажатия кнопки ОК, появится следующее окно.
image

Нам предлагают либо записать последовательность действий с помощью специального инструмента Coded UI Test Builder, либо использовать уже готовую последовательность.
Нас интересует первый вариант. Ок. В правом нижнем углу экрана открылся Coded UI Test Builder.
image

На нем, кроме закрытия и справки, всего 4 кнопки. О них по порядку.

Начать запись.
image

Просмотреть записанные шаги.
image

С помощь этой кнопки мы можем изменить карту пользовательского интерфейса (на карте представлены все элементы управления пользовательского интерфейса, отображаемые в тестируемом приложении). И добавить утверждение (assert).
image

И, наконец, сгенерировать код.
image

Пока закроем Test Builder и вернемся обратно в студию.

Автоматически в проект добавляются ссылки на следующие базовые сборки.
image

А так же пустой файл CodedUITest1.cs. В него мы и будем добавлять тестовые методы. Откроем его и создадим метод StartApp(), в котором будет запускаться наше приложение. Чтобы снова запустить Test Builder, нажмем правую кнопку мыши внутри метода.

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

image

Атрибут [TestInitialize] говорит о том, что код, помеченный им, будет вызываться при каждом запуске тестового метода.

Exe-файл моего приложения лежит на рабочем столе. Нажимаем Start Recording и запускаем приложение.
Посмотрим на записанные действия.
image

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

Введем имя для метода.
image

Вот что у нас получилось.
image

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

Прежде чем посмотреть реализацию метода StartRecordedMethod() еще раз глянем в Solution Explorer.
image

У нас появились 3 новых файла:
  • UIMap.uitest – XML-файл, содержащий все элементы управления на UI Map.
  • UIMap.Designer.cs – описание класса UIMap (помечен модификатором partial), содержит кодовое представление файла UIMap.uitest. Автоматически генерируемый файл, что очень важно.
  • UIMap.cs – Файл класса UIMap. Все настройки карты пользовательского интерфейса должны производиться в этом файле.


Теперь посмотрим на реализацию записанного нами метода в файле UIMap.Designer.cs
image

Ничего сложного здесь нет, ApplicationUnderTest стандартный класс, наследуемый от UITestControl. Параметрами метода Launch являются просто строковые переменные, которые генерируются автоматически, в этом легко убедиться.
image

Но есть нюанс.

Если моё приложение положить в папку и попробовать записать его запуск, то мы получим несколько иной эффект.
image

Если немного вдуматься, то все встает на свои места. Мое приложение принадлежит открытому окну (папке) и записывается уже не запуск приложения (как такового, хотя он тоже произойдет), а Double Click на элементе, принадлежащем записываемому окну.

Чтобы с этим не заморачиваться, мы можем без проблем описать запуск приложения вручную либо создав соответствующий метод в файле UIMap.cs, либо прямо в тестовом методе в файле CodedUITest1.cs как показано ниже.
image

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

Посмотрим на реализацию метода CloseAppRecordedMethod().
image

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

Самое время записать взаимодействие с интерфейсом нашего приложения.

Создадим в файле CodedUITest1.cs метод InterfaceTestMethod() и пометим его атрибутом [TestMethod]

Действуем по проверенной схеме =)
image

Нажимаем на кнопку генерации кода и указываем имя, я назову метод InterfaceTestRecordedMethod()
image

Взглянем на реализацию.
image

Здесь мы видим еще один стандартный класс для взаимодействия с интерфейсом Keyboard. Через статический метод SendKeys мы сообщаем о нажатии на клавишу табуляции.

Наш тест практически готов. Не хватает лишь какой-нибудь проверки =)

Реализуем её. Добавим проверку для TextBox’а, в котором отображается результат, после нажатия на кнопку. Сделать это достаточно просто.

Снова откроем UI Test Builder из метода InterfaceTestMethod() и нажмем на третью кнопку слева (добавление утверждения). Скажу пару слов об интерфейсе открывшегося окна.
image

Все, что нам доступно для нажатия на данный момент, это кнопка в левом верхнем углу. Нажав ее мы увидим список элементов управления на нашей карте пользовательского интерфейса. На ней будут только те элементы, которые участвовали при записи действий в метод InterfaceTestRecordedMethod(). Выбрав какой-либо элемент в списке, справа мы увидим его свойства. Так же станут активны элементы «Add assertion» и два элемента справа от него (с помощью первого можно обновить список свойств, а с помощью второго – изменять текущий выбранный элемент).

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

Нужного нам элемента управления (TextBox3) на карте, естественно, нет. Чтобы добавить его, нам необходимо, удерживая левую кнопку мыши, перетащить “прицел” на наш TextBox. Элемент будет помечен синей рамкой и добавлен в список элементов, затем нажмем Add control to UI Control Map (Alt + C) для добавления элемента на карту.
image

Примечание: здесь же мы можем переименовать и удалить выбранный элемент.

Для добавления утверждения выберем интересующее нас свойство и нажнем Add Assertion.
image

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

Сгенерируем код. Я назову метод ResultAssertEqualMethod().
image
image

Реализация:
image

Обычный Assert, первым параметром (ожидаемое значение) которому передается переменная со значением “Visual Studio 2010, Hey!”, а вторым (действительное значение) свойство Text нашего элемента управления.

Примечание: у такого подхода есть большой минус. При автоматической генерации методов, включающих утверждения, у нас нет возможности передавать дополнительные параметры в статические методы (например AreEqual()). Но мы можем сделать все самостоятельно в файле UIMap.cs или же в CodedUITest1.cs.
image

Все готово. Можно запускать! (Хотя никто и не запрещал сделать это и раньше).


Результаты не заставят себя долго ждать.
image

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

Откроем Test View и в нем выберем наш тестовый метод.
image

Нам покажут свойства метода, среди них нас интересует Data Connection String. Кликнем на многоточие справа от свойства. Откроется Wizard, в котором предлагается выбрать тип источника данных.
image

У меня есть заранее заготовленный XML-файл, поэтому я выберу 3 вариант. И нажму Next >

Содержание моего XML-файла:
image

Указываем путь до файла и сразу можем увидеть набор данных. И еще раз нажимаем Next >
image

Выбираем таблицу, Next >
image

И нам любезно предлагают добавить файл в проект. Соглашаемся.
image

Наш тестовый метод помечается следующими атрибутами:
image

Теперь данные из моего XML-файла доступны через TextContext. Но нам еще нужно их каким-то образом передать в методы InterfaceTestRecordedMethod() и ResultAssertEqualMethod(). Реализованы эти методы в файле UIMap.Designer.cs, поэтому изменить их мы не сможем. Точнее, сможем, но ненадолго (до следующей генерации кода).

Пойдем другим путем.

Воспользуемся еще одним новшеством, функцией Navigate To и поищем сгенерированные Builder'ом методы.

image
Для передачи параметров в методы, записывающие последовательность действий, генерируется класс XXXParams, где XXX – название метода.

image
А для передачи параметров в методы утверждений – XXXEpectedValues.

Вернемся к нашему тестовому методу InterfaceTestMethod() и изменим его следующим образом:
image

Запускаем тест.
image

Примечание: если вы достаточно внимательны, то могли заметить, что не смотря на два набора данных для теста, в результате написано, что выполнено 3 из 3:
image
Связано это с тем, что выполнение теста со всеми наборами данных тоже считается тестом.

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

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

Изначально предполагалось сделать вывод результата не в TextBox, а в MessageBox.
Но вот что получилось, с разным содержанием MessageBox'а:
image

Каждый раз создавался новый элемент управления на UI Map. Т.е. если бы я брал данные из того же XML, не добавив элемент с этими данными на карту, то тест бы проваливался. Согласитесь, не очень-то удобно =)

Нельзя записать последовательность действий и добавить assert в один метод с помощью UI Test Builder'a, что тоже не очень-то удобно.

И, наконец, я не смог удалить сгенерированный UI Test Builder'ом метод с записанной последовательностью действий.

UPD: Тестовый проект можно скачать тут.
+40
47.1k 92
Comments 25