Автотестирование активно развивается для web- и mobile-проектов. Но десктопные приложения приходится тестировать до сих пор: их по-прежнему используют финансовые компании, производства, сегмент HoReCa. Зайдите в ближайший банк, кафе, ресторан - всюду вы увидите Windows Desktop. При этом тестирование таких приложений почти не развивается, информации о нем мало.
Эта статья написана по опыту одного из наших проектов: необходимо было тестирование Windows Desktop приложения с десятками тысяч строк кода. Ручная проверка подобного решения заняла бы слишком много времени, поэтому мы решили разработать пилотную версию автотестов для выполнения проекта.
Подбирая инструмент для этой задачи, мы столкнулись с тем, что функции коммерческих студий ориентированы, в основном, на web- и mobile-, сами решения неоправданно удорожают проект, и не до конца понятно, что у них "под капотом". Мы стремимся к тому, чтобы до конца понять: что именно мы предлагаем клиенту, как используем, поэтому обратились к поиску инструментов с открытым кодом и выбрали оптимальный.
Итак, нашей задачей было создать пилотную версию АТ (автотестов). Конечной целью — автоматизация тестирования сложного .NET desktop приложения. При подборе инструмента выдвигали следующие требования:
-
Оперативность. Возможность "на выходе" просматривать отчеты из CI с ночными прогонами этих шести тестов (или получать письмо с описанием “упавших” тестов).
-
Прозрачность. Нужно, чтобы команда тестирования понимала, что именно проверяется в каждом шаге теста, а менеджер мог читать отчет в понятной для него форме (например: "на окне n не хватает третьего поля ввода").
-
Стоимость. Необходим инструмент, который не удорожает проект без необходимости.
Сравнение бесплатных фреймворков
Первым решением, опробованным для наших задач, стал Visual Studio Coded UI.
Плюсы:
-
Те же технологии в тестировании, что и в разработке продукта.
-
Есть test recorder.
-
Работа с WinForms.
-
Работа с UI-контролами DevExpress.
Минусы:
-
Небольшой объем документации.
-
Платная VS.
-
Test recorder генерирует плохо поддерживаемый код: тесты после записи и запуска сразу “падают” (возможно, не воспроизводится на “простых” интерфейсах).
-
BDD-написание тест-кейсов (с использованием, например, одного из самых популярных фреймворков на языке C# — Specflow) несовместимо “из коробки” с Coded UI.
-
Ограничения поиска и работы с элементами пользовательского интерфейса, накладываемые UIA API v.1 — MSAA.
Фреймворк CUITe может использоваться в дополнение к инструменту Coded UI под разные версии VS. Можно взять инструмент на заметку, но серьезных недостатков Coded UI он не исправляет.
Фреймворк TestStack.White — второе бесплатное решение, которое мы рассмотрели.
Плюсы:
-
Инструмент для полноценного написания тестов на языке C#.
-
Совместим с фреймворками BDD.
-
MSAA, UIA 2, 3 — на выбор.
Минусы:
-
“Заброшенная” кодовая база явно нуждается в рефакторинге.
-
Апдейт Nuget пакета не проходил с 2014 года, но в репозитории есть изменения.
Дальнейший поиск привел команду к фреймворку WhiteSharp.
Плюсы:
-
Инструмент для полноценного написания тестов на языке С#.
-
Совместим с фреймворками BDD.
Минусы:
-
Нет возможности нормально идентифицировать все элементы UI. Не подходит к DevExpress элементам.
-
Апдейт Nuget-пакета не происходил с 2016 года.
-
Немного документации, небольшое комьюнити.
-
Медленное исполнение элементарных кейсов (“открыть приложение”, “проверить содержимое строки статуса”)
У Winium.Desktop оказался большой минус — отсутствие поддержки с .NET 4.0
FlaUI — инструмент для полноценного написания тестов на языке С#. Автор подчеркивает, что он хотел переписать с чистого листа TestStack.White, убрав ненужное и устаревшее.
Плюсы:
-
Совместим с фреймворками BDD
-
Возможность использовать любую версию UIA: MSAA, UIA2 и UIA3
-
Поддерживаемая версия ОС Windows 7, 8, 10 (XP, скорее всего тоже, но мы не пробовали).
-
Хорошее комьюнити: решение проблем не только в issues, но и в чатах.
WinAppDriver + Appium — инструмент для полноценного написания тестов на языках разработки: C#, Java, Python.
Плюсы:
-
Разработан и поддерживается Microsoft.
-
Совместим с фреймворками BDD.
-
Невысокий порог вхождения, если был опыт написания тестов для веба или мобильных платформ.
Минусы:
-
Поддерживаемая версия ОС Windows 10+.
Взвесив плюсы и минусы решений, мы остановились на WinAppDriver’e и FlaUI.
Пример работы с двумя выбранными решениями
Перед стартом написания тестов нужно выбрать инспектор UI-элементов. К примеру, здесь. Наши QA-специалисты предпочитают Inspect.exe и UISpy.exe.
WinAppDriver
Системные требования: Windows 10 (более новые версии ОС, к сожалению, не поддерживаются). Разумеется, необходимо скачать, инсталлировать и запустить WinAppDriver.exe. Затем мы поставили на машину Appium, запустили Appium Server (“демон”). Если не хочется писать на C# — напишем, например, на Python+Robot.
Создаём сессию приложения и подключаемся к ней:
1 2 3 4 5 6 7 8 9 10 | class App: def __init__(self): desired_caps = { "app" : "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App" } self.__driver = webdriver.Remote( desired_capabilities=desired_caps) def setup(self): return self.__driver |
Опишем шаг запуска приложения:
1 2 3 4 5 | class CalculatorKeywordsLibrary(unittest.TestCase): @keyword ( "Open App" ) def open_app(self): self.driver = App().setup() return self.driver |
Инспектором находим кнопки и кликаем по ним:
1 2 3 | @keyword ( "Click Calculator Button" ) def click_calculator_button(self, str): self.driver.find_element_by_name(str).click() |
Описываем шаг получения результата:
1 2 3 4 5 6 7 | @keyword ( "Get Result" ) def getresult(self): display_text = self.driver.find_element_by_accessibility_id( "CalculatorResults" ).text display_text = display_text.strip( "Display is " ) display_text = display_text.rstrip( ' ' ) display_text = display_text.lstrip( ' ' ) return display_text |
Шаг выхода из приложения:
1 2 3 | @keyword ( "Quit App" ) def quit_app(self): self.driver.quit() |
В итоге, Robot-тест проверки вычисления квадратного корня будет выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Test Square [Tags] Calculator test [Documentation] *Purpose: test Square* ... ... *Actions*: ... — Check is the calculator started ... — Press button Four ... — Press button Square ... ... *Result*: ... — Result is 2 ... Assert Calculator Is Open Click Calculator Button Four Click Calculator Button Square root ${result} = Get Result AssertEquals ${result} 2 [Teardown] Quit App |
Запускать тест можно как обычный робот-тест из командной строки: robot test.robot. Существует подробная документация по BDD Robot Framework. Возможно тестирование приложений из WinStore и классических приложений.
Поддерживаемые типы локаторов:
-
FindElementByAccessibilityId
-
FindElementByClassName
-
FindElementById
-
FindElementByName
-
FindElementByTagName
-
FindElementByXPath
Можно сделать вывод, что это полностью Selenium-like тест. Можно применять любые паттерны тестирования: Page Object и т.д., добавить BDD framework (для Python, например, это Robot Framework или Behave).
Для дальнейшего изучения: подробная документация с примерами тестов на разных языках.
FlaUI
Фактически является “обёрткой” над UI Automation libraries. Коммерческие утилиты, разобранные раньше, также являются “врапперами” над предоставленным Microsoft API для автоматизации.
Windows 7 PC, VS. Запускаем приложение и аттачимся к процессу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [Test] public void NotepadAttachByNameTest() { using (var app = Application.Launch( "notepad.exe" )) { using (var automation = new UIA3Automation()) { var window = app.GetMainWindow(automation); Assert.That(window, Is.Not.Null); Assert.That(window.Title, Is.Not.Null); } app.Close(); } } |
Инспектором находим поле ввода и отправляем в него строку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | [Test] public void SendKeys() { using (var app = Application.Launch( "notepad.exe" )) { using (var automation = new UIA3Automation()) { var mainWindow = app.GetMainWindow(automation); var textArea = mainWindow.FindFirstDescendant( "_text" ); textArea.Click(); //some string Keyboard.Type( "Test string" ); //some key: Keyboard.Type(VirtualKeyShort.KEY_Z); Assert.That(mainWindow.FindFirstDescendant( "_text" ).Patterns.Value.Pattern.Value, Is.EqualTo( "Test stringz" )); } app.Close(); } } |
Основные виды поиска элементов:
-
FindFirstChild
-
FindAllChildren
-
FindFirstDescendant
-
FindAllDescendants
Можно искать по:
-
AutomationId
-
Name
-
ClassName
-
HelpText
Можно реализовать поиск по любому поддерживаемому элементом паттерну, например, по Value или LegacyAccessible. Или по условию.
1 2 | public AutomationElement Element(string legacyIAccessibleValue) => LoginWindow.FindFirstDescendant( new PropertyCondition(Automation.PropertyLibrary.LegacyIAccessible.Value, legacyIAccessibleValue)); |
Наша команда особенно оценила класс условных ожиданий
Retry.While(foundMethod, Timeout, RetryInterval);
Внутри — выполнение в цикле входящего метода через заданный интервал на протяжении заданного таймаута. Больше никаких слипов основного потока исполнения.
Пример:
1 2 3 4 5 6 7 8 | private static LoginWindow LoginWindow { get { Retry.WhileException(() => MainWindow.LoginWindow.IsEnabled); return new LoginWindow(MainWindow.LoginWindow); } } |
Создаем объект LoginWindow, только когда логин окно действительно отображается (интервал и таймаут — дефолтные). Такой подход избавляет от нестабильных тестов.
Для дальнейшего изучения: подробная документация на Github.
Сроки разработки тестов. Поддержка, стабильность UI-тестов
На шесть довольно объемных Regress-сценариев из примера было заложено три месяца разработки с “нуля”, включая ознакомление проектом, исследование доступных, описанных выше, фреймворков, создание скелета проекта АТ, настройка CI (continuous integration), менеджмент и коммуникации, сопроводительную документацию. Проект выполнен и сдан в срок.
При продолжении покрытия тестовых сценариев автотестами подготовительные операции, разумеется, будут занимать меньше времени, так как были реализованы на стадии пилота.
Помещаем тесты в Continuous Integration
Оба фреймворка поддерживают запуск тестов с использованием систем непрерывной интеграции. В нашей команде использовали TC и TFS.
При использовании фреймворка FLAUI, фреймворка SpecFlow и тестов, написанных на языке C#, в создании сборки нет ничего особенного (svc — build — run), кроме соблюдения некоторых условий на агенте исполнения тестов:
-
Агент сборки запускается как утилита из командной строки (вместо сервиса), обе системы это поддерживают. Необходимо установить автозапуск такого агента.
-
Automatic logon setup (Use SysInternals Autologon for Windows as it encrypts your password in the registry).
-
Отключить screensaver.
-
Отключить Windows Error Reporting. Справка на Gist: DisableWER.reg
-
Необходимо иметь на машине, где производится сборка и прогон тестов, активную десктопную сессию. Достаточно использовать для этого стандартное Windows-приложение.
-
RDC. Но часто можно встретить и совет пользоваться VNC (с аргументацией что: при подключении через Удаленный рабочий стол и последующем отключении, десктоп будет заблокирован When you connect using Remote desktop, then disconnect, the desktop will be locked, and UI Automation will fail)
BDD-фреймворк Specflow имеет интеграцию “на лету” c TC и TFS. Т.е. непосредственно в прогоне тестов отображается отчет об успешных и проваленных тестах.
Пример ROI автотестов
Перед внедрением автоматизации подсчитать ROI сложно, поскольку существует огромное количество неизвестных факторов и коэффициентов. (И лучше не верить калькуляции ROI в разделе прайсинга Test Complete).
Но после реализации пилотного проекта автоматизации можно привести некоторые цифры. Возьмем самую простую формулу ROI:
ROI = (Gain of Investment — Cost of Investment) / Cost of Investment
Стоимость проекта автоматизации за первый год (без поддержки):
3 месяца на покрытие автотестом Regress-прогона при работе одного инженера-разработчика в тестировании: получается, 500 часов * Dev-ставка в час. Допустим, стоимость решения (Cost of investment) оплачивается заказчиком полностью в первый год (не делится по частям, и на покупку решения не берется кредит).
Выгода:
В первой статье мы подсчитали, что чистый “прогон” регресс-тестов вручную занимает 1 месяц: 2 недели первичный прогон + 2 недели вторичный прогон (без проверки новых тестов и сопутствующих расходов).
После разработки АТ-регресса (3 месяца), 9 месяцев в году прогоны регресс-теста руками не исполняются. Будем считать, что высвободившиеся ресурсы подключили к другим проектам в компании. Сохранили: 1500 часов * QAставка в час * 2 человека в команде.
ROI = (1500*QA*2 — 500*Dev) / 500 * Dev = (6*QA — 1*Dev) / Dev
При взятии по рынку средних значений QA ставка = n, Dev ставка = 1,5n получаем:
ROI = (6n — 1,5n) / 1,5n = 3.
Первый год, очевидно, всегда отличается меньшей окупаемостью, показатели последующих лет будут значительно отличаться из-за отсутствия Cost.
Разумеется, это самый общий подсчет, не учитывающий поддержку, амортизацию, доработку отчетности, а также качество самих автотестов. Второй подсчет ROI рекомендуется проводить после первого года использования АТ с учетом этих дополнительных фактов.
Результат
Разработанные автотесты заменяют ручные силы команды QA и высвобождают команду из двух человек для более важных проектных задач. При этом оптимально расходуются ресурсы проекта.
Да, мануальное тестирование — необходимая часть разработки. Но если ПО состоит из десятков тысяч строк кода и требует двух-трех недель от разработки новой функции до релиза, автоматизация очень важна. Чтобы понять, выгодно ли покрытие всех работ автотестами и возможно ли оно, наша команда применяет стратегию Do Pilot: то есть, автоматизирует участок работ при помощи выбранного фреймворка.
Тем не менее, если использовать в платных “коробочных” решениях для АТ все нужные функции и масштабировать команду хотя бы до двух инженеров — поддержка таких решений удорожает проект. Чтобы на заказчика ПО не ложилась необходимость платить в начале за само решение, а потом — каждый год — за его поддержку, мы рассмотрели несколько фреймворков для АТ с открытым кодом. Из предложений выбрали те, которые нас устраивают. Два найденных решения использовали для работы.
Автоматизация тестирования, хотя и требует подготовки по поиску фреймворка и разработке самих автотестов, оказывается значительно выгоднее ручных “прогонов”.