Автоматизация тестирования – неотъемлемая часть процесса обеспечения качества. Мы в нашей практике чаще всего разрабатываем тесты для веб-, мобильных приложений и API, но сегодня хотим рассказать о более редком направлении – тестировании десктоп-приложений.

Кратко рассмотрим подходы, инструменты, технологии и «грабли», на которые можно наступить при выполнении этой задачи. Статья будет полезна специалистам, которые хотят попробовать автоматизировать ежедневную монотонную работу, а также коллегам по цеху в сфере автоматизации gui-тестирования – как начинающим, так и разработчикам с опытом.

По архитектуре большинство десктоп-решений можно разделить на слои UI, Business, Transport и DB, а по их соотношению – на следующие группы:

  • standalone-приложения (все слои в одном месте);

  • клиент-серверные – тонкий клиент» (ui на клиенте, остальное на сервере); 

  • «толстый клиент» (ui и business на клиенте, db-сервер). 

Подобные решения чаще всего реализуют в web, чтобы обеспечить их гибкость, кроссплатформенность, легкость в разработке и поддержке. Из-за этой тенденции становится меньше десктоп-проектов, как и запросов на автотесты для них – по нашим наблюдениям, их доля не превышает 5-10% среди других проектов, поэтому для поиска актуального how-to гайда иногда нужно немало времени. Однако, в энтерпрайзе по-прежнему много десктопных приложений, качество работы которых оказывает влияние на бизнес.

Особенности

Рассмотрим несколько особенностей, которые мы учитываем при создании автотестов для десктопа:

  • Для начала нам нужен активный десктоп. Некоторые методы не смогут отработать, если запустить тесты через RDP и свернуть это окно. К счастью, для этого есть “костыли”: 

    • перехват сессии rdp через tscon на саму себя позволит оставить рабочий стол активным с отключенным соединением; 

    • небольшая правка реестра на хосте оставит активным рабочий стол на удалённой машине, что обеспечит возможность свернуть окно rdp-сессии.

  • На первый взгляд может показаться, что при работе с приложением с различными локализациями существуют затруднения: например, что некоторые локаторы будут находиться по имени элемента, которое может различаться в разных локализациях. Однако, на практике эти элементы достаточно легко найти по определенным критериям (тип, automation_id, активности и т.д.).

  • Поиск нужного элемента иногда выливается в интересную историю. Если открыть несколько разных приложений, то число элементов в дереве по ним может добраться до десяти тысяч, что значительно замедлит поиск отдельного элемента. Для того чтобы этого избежать, принято использовать поиск элементов по системе «родитель-наследник». Например, исходное окно приложения будет «родительским» элементом, а тулбар в нём – «дочерним». По мере «хождения» по приложению можно существенно ускорить поиск, отсекая лишние элементы и оставляя только нужную нам ветку. Данный подход имеет свои особенности. Например, некоторые приложения могут кидать popup-окно вне дерева элементов вкладки и даже самого приложения. В таком случае, именно рабочий стол следует принимать как исходный родительский элемент и работать от него.

  • Взаимодействие с элементами в случае MS UIA может происходит через обычные COM-интерфейсы, но допускается использовать и нестандартные, и это не редкость. Для Python есть библиотека comtypes, которая в связке с CPython решает такие вопросы. 

  • Хотя технология MS UIA и имеет относительно подробную документацию, понять её «с лёту» может быть трудно. Для быстрого погружения можно воспользоваться разделом How-to Topics с полезными советами (на английском, само собой) и примерами кода (С#), но не стоит возлагать на него лишние надежды.

  • Комьюнити меньше, чем в других направлениях (web, mobile, api), поэтому вы можете столкнуться с недостатком информации.

Подходы 

Существуют три основных подхода к автотестам desktop-приложений: координатный метод, распознавание изображений и accessibility. Все реализующие их инструменты взаимодействуют с первым слоем, UI.

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

Распознавание изображений – технология поиска местоположения на экране на основе изображения области или отдельного элемента. Хотя этот метод с каждым годом развивается и совершенствуется, на данный момент его инструменты все еще бывают нестабильны и “прожорливы”. В некоторых кейсах возможно получить значение поля с помощью методологий распознавания текста. 

Accessibility-метод – достаточно стабильный и совершенный. Позволяет удобно управлять приложением во время теста, получать и использовать любые атрибуты элементов. Используется привычный подход с локаторами. Сами локаторы можно детектить с помощью инструмента Inspect из пакета Windows SDK.

Инструменты

На Github в топ инструментов для автоматизации десктопа входят следующие: 

  1. RobotJS – фреймворк для JavaScript (координатный подходом);

  2. pyautogui – Python-фреймворк (координатный подход + распознавание изображений);

  3. AutoHotkey (C++) – keyword-driven фреймворк (accessibility);

  4. Appium Desktop – один из самых популярных фреймворков для автоматизации мобильных приложений (accessibility);

  5. pywinauto – фреймворк для Python (accessibility).

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

1) Pyautogui

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

  • Как получить текущую позицию курсора:

>>> pyautogui.position()
Point(x=324, y=1149)
  • ...или размер экрана в целом:

>>> pyautogui.size()
Size(width=2560, height=1440)

При этом размеры второго экрана выводятся некорректно. Кликаем дважды по точке на экране:

>>> pyautogui.doubleClick(x=324, y=1149, button="left")
  • Фреймворк позволяет искать область на основе заранее подготовленного скриншота:

>>> pyautogui.locateOnScreen("test.png")
Box(left=79, top=1159, width=31, height=32)
  • Также можно найти целый список областей:

>>> pyautogui.locateAllOnScreen("test.png")
<generator object _locateAll_python at 0x04975FB0>

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

2) Lackey (SikuliX)

Библиотеку Lackey мы выбрали как пример инструмента для реализации подхода с распознаванием изображения. Она работает на основе библиотеки Sikuli с использованием “великого и ужасного” OpenCV для распознавания элементов на экране. Сам OpenCV поддерживает, например, технологию CUDA, которая может дать ускорение распознавания в 5-100 раз. Вычислительные мощности видеокарт имеют стабильный ежегодный прирост, и подобные технологии дают хорошую прибавку. 

  • Клик по распознанному элементу:

>>> lackey.click("test.png")
[info] Found math for pattern 'test.PNG' at (251,1368) with confidence (1.0). Target at (309,1371)
[action] Clicked at (Location object at (309,1371))

Документация Lackey составлена достаточно подробно, однако, отдельные вопросы – например, прикручивание CUDA – приходится искать отдельно. 

3) Pywinauto

Одна из популярных библиотек в рамках accessibility-подхода. На данный момент поддерживает технологии Win32 API и UIA, но есть подвижки в сторону AT SPI.

  • Запуск приложения:

>> application = Application(backend="uia").start("explorer.exe").top_window()

Ни для кого не секрет, что поиск рабочих локаторов процесс довольно трудоемкий. В проекте мы использовали инструмент Inspect, он входит в пакет Windows SDK и скачивается отдельно. Фреймворк позволяет нам искать элементы по критериям, которые с лёгкостью можно получить “на лету” через этот инструмент:

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

В числе удобных “фишек” инструмента: вывод границ элемента и краткий список атрибутов сразу при наведении, быстрый переход к родительским/дочерним элементам.

Локаторы можно искать и иными путями. Например, список элементов и критерии для их поиска может печатать сам фреймворк через функцию print_control_identifiers.

  • Открываем закладку бокового меню:

>> application.child_window(title=”Загрузки”, control_type=”TreeItem”).select()

И ожидаем открытия списка с файлами:

files_list_element = application.child_window(title=”Просмотр элементов”, control_type=”List”)
files_list_element.wait(“ready”, timeout=3)

Файлов в нашей директории очень много. Ускорим поиск файла, используя родительский элемент из предыдущего шага, и проверим его активность:

assert files_list_element.descandants(title=”test.txt”)[0].is_enabled()

Методы в документации описаны довольно подробно, у вас не будет особых трудностей с тем, чтобы разобраться в коде (до момента с подключением comtypes). 

Технологии

Фреймворки, которые мы перечислили выше, это своеобразные “обёртки” для технологий, обеспечивающих управление на стороне ОС:

  • Windows: Win32 API, MS UI Automation

  • Linux: AT SPI (QT)

  • Mac: Apple Accessibility API

Расскажем подробнее о технологиях под Windows, т.к. подавляющее число gui-приложений пишут под эту ОС.

Технология

Поддерживаемость архитектур построения приложений

Читаемость локаторов

Производительность

Win32 API

Частично Windows Forms, без WPF

Средняя

Быстро

MS UIA

Все приложения

(редкие исключения)

Хорошая

Сравнительно медленно

Особенности архитектуры тестового фреймворка

В нашей практике мы чаще всего применяем паттерн Page Object. Однако, так как в десктопе в большинстве случаев вся архитектура строится как “слоистая”, при таком подходе мы получаем достаточно длинную наследуемую цепочку из родительских элементов. Поэтому для того чтобы облегчить читаемость кода, нужно прокачать его до Page Elements, где среди «пейджей» выделяются общие элементы и выносятся в отдельный объект. На этапе проектирования архитектуры важно выстроить иерархию классов (пейджей) и для этого изучить UI приложения. При этом с ростом количества тестов огрехи в архитектуре могут дорого стоить.

Не следует забывать и о паттернах проектирования. Тут нам точно пригодятся двое из ларца – “декоратор” и “посредник”, и вполне вероятно, что в эту компанию может затесаться синглтон.

“Декоратор” в разработке автотестов используется повсеместно. Чаще всего в фикстурах.

При переключении вкладки в одном из пейджей нам необходимо будет сообщить об этом другим пейджам. Тут на помощь придёт “посредник”.

При построении архитектуры автотестов для веб-приложения мы считаем, что у нас есть элемент и десяток методов для взаимодействия с ним. Десктоп “открывает дверь ногой” и представляет нам около 40 разных типов элементов (link): от простого checkbox до сложного DataGrid. Если говорить о количестве методов взаимодействия с каждым из типов, то тот же checkbox мы можем «выделить» 4 разными способами. Случается, что в приложении попадаются и «кастомные» элементы, и «общаться» с ними приходится путём “чёрной уличной магии” – обращения к низкоуровневому comtypes или нативной C#-библиотеке.

Возможные проблемы при автоматизации

Из-за взаимодействия с UI к списку можно смело добавить все «грабли» автоматизации тестирования UI. Как вишенка на торте, встречаются случаи, когда элемент приложения уже существует, но долго инициализируется – и вызов метода управления может не отработать корректно, так как элемент неактивен.

Есть довольно частный кейс с фреймворком pywinauto. Например, в приложении реализован поиск по товарам. Если результат поиска достаточно велик или весь список товаров выводится при открытии окна с поиском, то количество элементов в этом списке будет следующим: количество колонок*кол-во строк в результате*3 (в нашем кейсе). Если строк будет более двух сотен, то pywinauto не справится с таким количеством объектов. Есть подозрения, что и другие фреймворки accessibility-подхода имеют подобную особенность. Как вполне рабочий вариант, можно реализовать метод обхода дерева генератором через фреймворк и comtypes.

Вывод

В нашей статье мы рассмотрели проблематику и особенности тестирования desktop-приложений, с которыми столкнулись, кратко пробежались по инструментам, технологиям и подходам. В выводе хочется подсветить главный вопрос, который напрашивается после прочтения статьи: “стоит ли погружаться в автоматизацию десктопа?”. 

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

Кроме того, стоит учитывать и следующие особенности:

  1. в зависимости от окружения автотесты desktop-приложения могут быть менее быстрыми и  стабильными относительно остальных видов

  2. более требовательны к компетенциям DevOps и/или SDET в случае необходимости параллелизации или внедрения через CI/CD

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

Надеемся, что у нас получилось внести чуть больше ясности в эту область автоматизации. Будем рады, если наша статья окажется вам полезной. А если вам есть что сказать в ответ – ждем ваших комментариев!

Спасибо за внимание!

Комментарии (1)


  1. vasily-v-ryabov
    11.09.2021 11:41

    Спасибо! Интересно послушать независимое мнение про свою библиотеку. comtypes - да, там есть косяки, потихоньку тоже пытаюсь их исправлять. Не всегда удачно, но уже получше стало. Было дело, даже бага в питоне (точнее отсутствие фичи в Сишной libffi) сломала нам всё к чертям. Слава богу Python core девелоперы откатили коммит, и эти две версии питона уже ушли в прошлое.