>
>
Visual Studio Coded UI Tests: теория и …

Сергей Хренов
Статей: 39

Visual Studio Coded UI Tests: теория и практика применения в нашей компании

Автоматизированные тесты пользовательских интерфейсов - тема, к которой настороженно относятся даже опытные разработчики. При этом технология такого тестирования не представляет собой чего-либо экстраординарного, а в случае Visual Studio Coded UI Tests является расширением встроенной системы модульного тестирования Visual Studio Team Test. В этой статье я хочу остановиться как на теме UI-тестирования в общем, так и на нашем частном опыте применения Visual Studio Coded UI Tests в работе над статическим анализатором PVS-Studio.

Основы

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

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

У основания пирамиды находятся Unit-тесты. Действительно, такие тесты проще сделать ещё на этапе разработки, и они будут очень быстро выполняться. С другой стороны, на вершине пирамиды находятся автоматизированные тесты интерфейсов. Этих тестов не должно быть много, так как сложность их создания, а также время выполнения довольно велики. К тому же непонятно, кому доверить создание UI-тестов. Ведь по сути речь идет об эмуляции действий пользователя. Все это очень далеко от кода приложения, поэтому разработчики неохотно занимаются такой работой. А чтобы делать качественные автоматизированные тесты интерфейсов без участия (или с минимальным участием) программистов, требуется использование платных инструментальных средств. Совокупность всех этих факторов зачастую приводит к тому, что UI-тесты не делают вовсе, ограничиваясь однократным ручным тестированием новых функциональных возможностей. К тому же тесты интерфейсов крайне затратны не только на этапе разработки, но и в ходе дальнейшего жизненного цикла приложения. Даже незначительное изменение пользовательского интерфейса может привести к ошибкам выполнения множества тестов и необходимости их модификации.

Замечу, что в настоящее время наша система тестов в общем и целом соответствует рекомендациям. Число автоматизированных тестов GUI (45) составляет примерно десятую часть от общего числа тестов PVS-Studio. При этом число Unit-тестов у нас не так велико, но они дополнены массой других тестовых систем:

  • Тесты качества работы анализаторов (С/C++/C#/Java), в ходе выполнения которых производится проверка большого пула тестовых проектов на разных операционных системах (Windows, Linux, macOS) и сравнение логов новых предупреждений с эталонными;
  • Тесты специфических возможностей (отслеживание запуска компиляторов и т.п.);
  • Внешние тесты command-line приложений;
  • Тесты корректности сборки, установки и развёртывания;
  • Тесты документации.

На начальном этапе своего развития анализатор PVS-Studio представлял собой приложение для поиска ошибок при переносе C/C++ кода на 64-битную платформу. Да и назывался он в то время по-другому, "Viva64". С историей создания продукта можно ознакомиться в статье "Как 10 лет назад начинался проект PVS-Studio". После интеграции в Visual Studio 2005 анализатор обзавёлся графическим интерфейсом пользователя, по сути - интерфейсом самой IDE Visual Studio, в которой после установки плагина появлялось дополнительное меню для доступа к функциональности анализатора. Меню представляло собой два-три пункта, поэтому тестировать там было нечего. Да и Visual Studio на тот момент не содержал никакого встроенного инструментария для тестирования GUI.

Visual Studio Coded UI Tests и альтернативы

Всё изменилось с выходом Visual Studio 2010, в которой появилась интегрированная система создания UI-тестов: Visual Studio Coded UI Tests (CUIT). Базируясь на системе модульного тестирования Visual Studio Team Test, для доступа к визуальным элементам интерфейсов в CUIT изначально применялась технология MSAA (Microsoft Active Accessibility). В дальнейшем технология совершенствовалась и в настоящее время представляет собой развитую модель автоматизации пользовательского интерфейса для тестирования кода UIA (UI Automation). Она позволяет системе тестирования получать доступ к открытым полям (имя объекта, внутреннее имя класса объекта, текущее состояние объекта, его место в иерархической структуре интерфейса и т.п.) COM и .NET UI-элементов, а система позволяет эмулировать воздействия на данные элементы посредством стандартных устройств ввода (мышь, клавиатура). Сразу "из коробки" поддерживаются режимы записи действий пользователя при взаимодействии с интерфейсом (по аналогии с макросами Visual Studio), автоматизация построения "карты интерфейса" (свойства элементов управления, параметры их поиска и доступа к ним) вкупе с автоматической генерацией управляющего кода. В дальнейшем всю накопленную информацию несложно видоизменять и поддерживать в актуальном состоянии, а также настраивать тестовые последовательности по своему желанию, обладая при этом минимальными навыками программирования.

Более того, как я уже говорил ранее, сейчас при создании сложных интеллектуальных UI-тестов можно вообще обойтись без каких-либо навыков программирования, при условии использования специализированного платного инструментария. Ну а если нет желания или возможности использовать проприетарные тестовые среды, существует масса бесплатных продуктов и фреймворков. Система Visual Studio Coded UI Tests является промежуточным решением, позволяющим не только максимально автоматизировать процесс создания UI-тестов. С её помощью легко создавать произвольные тестовые последовательности на языках программирования C# или VB.

Всё это позволяет значительно снизить затраты на создание и поддержку актуальности тестов GUI. Используемый фреймворк несложен для понимания и в общем виде может быть представлен в виде диаграммы.

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

К основным возможностям CUIT от Microsoft можно отнести:

  • Функциональное тестирование интерфейсов пользователя. Поддерживаются классические Windows-based приложения, web-приложения и сервисы, WPF
  • Генерация кода (включая автоматическую) на языках VB/C#
  • Возможность интеграции в сборочный процесс
  • Локальные или удаленные запуски, сбор отчётности
  • Наличие средств записи и воспроизведения тестовых последовательностей
  • Хорошая расширяемость

Несмотря на ряд сложностей, связанных с CUIT, использование данной технологии тестирования является предпочтительным ещё по ряду причин:

  • Эффективное взаимодействие разработчиков и тестировщиков в рамках одного инструментального средства и языка программирования
  • Дополнительные возможности работы с "картой интерфейса", позволяющие производить идентификацию элементов управления "на лету", синхронизировать элементы и осуществлять дозапись тестовых последовательностей
  • Тонкая настройка механизма воспроизведения, позволяющая наряду с базовыми настройками, такими как задержка между операциями, таймаут поиска элемента и т.п., использовать в коде специализированные механизмы. Например, блокировку текущего потока до момента активизации (визуализации) элемента управления при помощи методов WaitForControlExist или WaitForReady с перечислением WaitForReadyLevel и т.п.
  • Возможность программирования неограниченно сложных тестов

Не буду далее вдаваться в теоретические аспекты технологии Visual Studio Coded UI Tests, все они подробно изложены в соответствующей документации. Там же вы сможете найти развернутые пошаговые инструкции по созданию простейшего UI-теста на базе этой системы. И да, система не бесплатна, для работы с ней потребуется Visual Studio Enterprise.

Описанная технология не является единственной представленной на рынке. Существует множество других решений. Все альтернативные системы UI-тестирования можно разделить на платные и бесплатные. При этом выбор конкретной системы не всегда будет зависеть от её цены. Например, немаловажным фактором может служить возможность создания тестов без необходимости программирования, но при этом тесты, возможно, не будут в достаточной степени гибкими. Также важна поддержка необходимой тестовой среды - операционных систем и приложений. Наконец, на выбор могут оказать влияние чисто специфические особенности приложения и его интерфейса. Приведу несколько популярных систем и технологий для тестирования GUI.

Платные: TestComplete (SmartBear), Unified Functional Testing (Micro Focus), Squish (froglogic), Automated Testing Tools (Ranorex), Eggplant Functional (eggplant) и др.

Бесплатные: AutoIt (windows), Selenium (web), Katalon Studio (web, mobile), Sahi (web), Robot Framework (web), LDTP (Linux Desktop Testing Project), Open source frameworks: TestStack.White + UIAutomationVerify, .NET Windows automation library и др.

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

В нашем случае проблема выбора не стояла: с выходом Visual Studio 2010 с дополнением Coded UI Tests стало возможным легко добавить в наше тестовое окружение набор функциональных тестов для проверки пользовательского интерфейса плагина PVS-Studio для Visual Studio.

UI-тесты PVS-Studio

Итак, тесты GUI в нашей компании применяются уже более 6 лет. Первоначальный набор UI-тестов для Visual Studio 2010 базировался на единственно доступной в тот момент технологии MSAA (Microsoft Active Accessibility). С выходом Visual Studio 2012 технология MSAA получила значительное развитие и в настоящее время носит название UIA (UI Automation). Было принято решение в дальнейшем использовать UIA, а тесты на базе MSAA оставить для тестирования работы плагина к Visual Studio 2010 (мы поддерживаем и тестируем плагины для всех версий Visual Studio, начиная с Visual Studio 2010).

В результате у нас образовалось две "ветки" UI-тестов. При этом в тестовом проекте обе этих ветки использовали общую карту интерфейса и разделяемый код. В коде это выглядело примерно так (метод для сброса настроек Visual Studio к стандартным перед запуском теста):

public void ResetVSSettings(TestingMode mode)
{
  ....
  #region MSAA Mode
  if (mode == TestingMode.MSAA)
  {
    ....
    return;
  }
  #endregion

  //UIA Mode
  ....
}

При внесении изменений в интерфейс плагина приходилось вносить правки в обе ветки UI-тестов, а добавление новой функциональности приводило к необходимости дублирования элемента интерфейса в карте: то есть создавать два разных элемента для каждой из технологий MSAA и UIA. Все это требовало много сил не только на создание или изменение тестов, но и на поддержание тестовой среды в стабильном состоянии. Остановлюсь на данном аспекте более подробно.

По моим наблюдениям, стабильность тестовой среды при тестировании GUI является значительной проблемой. В основном это связано с сильной зависимостью такого тестирования от множества внешних факторов. Ведь по факту производится эмуляция действий пользователя: нажатие на клавиши, перемещение курсора мыши, клики мышью и т.п. Здесь много чего может "пойти не так". Например, если в ходе выполнения теста кто-то будет взаимодействовать с клавиатурой, подключенной к тестовому серверу. Также, неожиданно разрешение монитора может оказаться недостаточным для отображения какого-либо элемента управления, и он не будет найден тестовой средой.

Ожидание:

Реальность:

Небрежно настроенный (и не найденный потом) элемент карты интерфейса - практически лидер проблемного поведения. Например, мастер построения карты интерфейса Visual Studio Coded UI Tests при создании нового элемента управления использует для него критерии поиска типа "Equals" по умолчанию. То есть требуется точное соответствие названий свойств заданным значениям. Обычно это работает, но иногда стабильность выполнения тестов можно значительно повысить, используя менее строгий критерий поиска "Contains" вместо "Equals". Это лишь один из примеров "тонкой настройки", с которой вы можете столкнуться при работе над UI-тестами.

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

С подобной задачей мы столкнулись в ходе работы над одним из тестов нашего плагина. В этом тесте сначала открывается пустая среда Visual Studio, затем туда загружается некое тестовое решение, которое целиком проверяется при помощи PVS-Studio (меню "PVS-Studio" -> "Check" -> "Solution"). Проблема состояла в определении момента, когда проверка будет завершена. В зависимости от ряда условий, проверка может занимать не всегда одинаковое время, поэтому простые таймауты здесь не работают. Также нельзя воспользоваться штатными механизмами приостановки работы тестового потока и ожиданием появления (или скрытия) какого-либо элемента управления, так как не к чему привязаться. В ходе проверки появляется окно со статусом работы, но это окно можно скрыть, и проверка будет продолжаться. Т.е. на это окно нельзя ориентироваться (к тому же у него есть настройка "Не закрывать после завершения анализа"). А алгоритм хотелось сделать более общим для того, чтобы по возможности иcпользовать его для различных тестов, связанных с проверкой проектов и ожиданием завершения этого процесса. Решение было найдено. После запуска проверки и до момента её завершения тот самый пункт меню "PVS-Studio" -> "Check" -> "Solution" является неактивным. Нам оставалось через некий интервал времени проверять свойство "Enabled" этого пункта меню (через объект карты интерфейса) и в случае обнаружения, что элемент стал активным, считать процесс проверки решения завершённым.

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

Напомню, что система UI-тестов в нашей компании развивалась с 2010 года. За это время было создано несколько десятков тестовых последовательностей и написано много вспомогательного кода. К тестам плагина со временем добавились тесты приложения Standalone. К этому моменту старая ветка тестирования плагина для Visual Studio 2010 потеряла актуальность и была заброшена, но просто так вырезать этот "мёртвый" код из проекта было нельзя. Во-первых, как я показывал ранее, код был достаточно глубоко интегрирован в тестовые методы. А во-вторых, более половины элементов существующей карты интерфейса относились к старой технологии MSAA, но были повторно использованы (вместо дублирования) во многих новых тестах наравне с UIA-элементами (это возможно благодаря преемственности технологий). При этом к "старым" элементам была привязана масса как автоматически сгенерированного кода, так и содержимого тестовых методов.

К осени 2017 года назрела необходимость улучшения системы UI-тестов. В целом тесты работали нормально, но время от времени какой-нибудь тест "падал" по непонятным причинам. Точнее, причина обычно заключалась в поиске элемента управления. В каждом конкретном случае приходилось проходить по дереву карты интерфейса до конкретного элемента и проверять его критерии поиска и другие настройки. Иногда помогал программный сброс этих настроек перед выполнением теста. Учитывая разросшуюся к этому моменту (и во многом избыточную) карту интерфейса, а также наличие "мёртвого" кода, данный процесс требовал значительных усилий.

Некоторое время задача "ждала своего героя", пока, наконец, не попала ко мне.

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

В результате удалось примерно на 30% сократить объем кода тестовых методов, а число элементов управления в дереве карты интерфейсов уменьшилось вдвое. Но самое главное, UI-тесты стали показывать более стабильную работу и требовать меньше внимания. А падения стали чаще происходить по причинам внесения изменений в функциональность анализатора или при обнаружении несоответствий (ошибок). Собственно, для этих целей и нужна система UI-тестов.

Таким образом, в настоящее время система автоматических тестов интерфейса PVS-Studio имеет следующие базовые характеристики:

  • Visual Studio Coded UI Test
  • 45 сценариев
  • 4 095 строк кода тестовых методов
  • 19 889 строк auto-generated кода (без учета размера xml-файла хранения настроек UI Map)
  • 1 час 34 минуты выполнения (средняя величина по результатам последних 30 запусков)
  • Работа на выделенном сервере (запуск утилитой MSTest.exe)
  • Контроль работы и анализ отчета о выполнении (Jenkins)

Заключение

В заключение хочу привести список критериев успешности автоматических тестов GUI, который основан на анализе нашего опыта работы с этой технологией (некоторые из критериев применимы и для других технологий тестирования, например, Unit-тестов).

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

Качественная настройка инфраструктуры. Не экономьте при разработке карты интерфейсов. Упростите работу фреймворка при поиске элементов, тщательно описав все их свойства и задав интеллектуальные критерии поиска. Уделите внимание возможностям дальнейшей модификации.

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

Простые и независимые интеллектуальные тесты. Чем проще будут ваши тесты, тем лучше. Старайтесь делать отдельный тест для проверки конкретного элемента управления или моделируемой ситуации. Также обеспечьте независимость тестов друг от друга. Падение одного из тестов не должно оказывать влияния на весь процесс.

Понятные имена. Используйте префиксы в названиях однотипных тестов. Многие среды позволяют запускать тесты с фильтрацией по имени. Также используйте группировку тестов по возможности.

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

Анализ выдаваемых отчётов. Обеспечьте простую и наглядную форму получения отчетности о проделанной работе. Используйте системы непрерывной интеграции для диспетчеризации тестов, а также оперативного получения и анализа результатов тестирования.