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: Тестовый проект можно скачать тут.
    Поделиться публикацией
    Комментарии 25
      0
      Вы сошли с ума, спрячьте под кат! :)
        +4
        Прошу прощения, теперь все в порядке?
          +1
          ОК
          0
          это еще мягко сказано )))
          –8
          Даже не поленюсь повторить vedburtruba и его первый комментарий — ПОД КАТ!
            +5
            Спасибо, очень стоящая статья.
              0
              И вам =) за первый положительный комментарий.
              0
              Ну вот, Вы опередили моего коллегу, который совсем недавно просил инвайт, чтобы написать про эту фичу VS2010. Теперь ему придётся поглубже зарыться, чтобы статью написать :)

              Расписано отлично, намного понятнее, чем на словах (хотя не удивительно — скриншоты решают :)
              Спасибо за статью!
                +4
                Думаю, .Net-овстких просторов всем хватит, удачи и вдохновения вашему другу! =)
                0
                Пожалуйста, смените хостинг для картинок.

                Их не видно. Например, положите на habreffect.ru/
                  0
                  Спасибо, сделаю.
                    0
                    Спасибо, с картинками стало интересной статьёй ))) Плюсанул )
                  0
                  Спасибо, интересно, единственное что опять же это MStest, не знаю как на это отреагируют… хотя в данном случае — некритично. А не подскажите, чем можно аналогично тестировать Java-интерфейсы?
                    0
                    Когда читал в голове тоже всплыла мысль про то, как бы это запустить на «не MSTest» Unit фреймворке. Думаю, что сделать это, скорее всего, возможно.

                    Правда использую MSTest для Silverlight — хватает с головой сейчас. Не удобно, что на сервере nUnit, а на клиенте MSTest, на клиент другого ничего нет, вот и думаешь, а не перелезть ли вообще на MSTest.
                      0
                      Извините, не подскажу.
                      –2
                      Не осилил.
                      • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Кстати, есть специальный блог WPF — habrahabr.ru/blogs/wpf/
                          0
                          12 секунд на 2 теста? Трудно назвать способ быстрым. А если у меня куча тестов? Успею попить чай?
                            +1
                            С вашего позволения я рассмотрю этот вопрос более детально в следующей статье, спасибо.
                              0
                              Интересная статья. Раз думаете над следующей, то рассмотрите еще вариант скринкаста — воспринимается куда лучше чем столько картинок =)
                                0
                                Учту, спасибо.
                              0
                              Тесты вообще лучше гонять по ночам.
                                0
                                По моему, не лучше.

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

                            Самое читаемое