| Как я перестал бояться GUI-тестов и научился их любить (почти) |
| 15.02.2026 14:18 |
|
Автор: Кирилл Толмачев
В феврале этого года я писал на Хабре про автоматизацию тестов для САПР. Мы делали систему с записью действий в JSON и воспроизведением через pyautogui. Работало. Но только для одного конкретного проекта. С тех пор фреймворк вырос. Сильно. Из узкоспециализированного решения для промышленного ПО превратился в универсальный инструмент. Теперь работает с чем угодно - офисные пакеты, банковские клиенты, CAD-системы. Что изменилось? Убрал привязку к конкретному софту. Добавил умный поиск элементов вместо тупых координат. Сделал так, чтобы QA мог записать тест без единой строки кода. Прикрутил UI-ассерты, мониторинг системы, файловые проверки. Короче, то что начиналось как решение для одной задачи, выросло в полноценный фреймворк. И оказалось полезным не только мне. Что меня бесило в обычных GUI-тестах Давайте честно. Если вы хоть раз писали UI-автотесты, вы знаете боль:
Я работал в проекте, где были виртуальные рабочие места с кучей импортозамещённых приложений. Windows-окружение, куча legacy софта, ручной регресс занимал недели. И думал: "Должен же быть способ проще?" Про культуру (или её отсутствие)Знаете, что меня удивляло на прошлых местах работы? Никто не делился своими наработками. Серьёзно. У кого-то был крутой скрипт для автоматизации - молчок. Кто-то запилил фреймворк - держит при себе. Люди боялись. Боялись, что их легко заменят, если покажут свои инструменты. Ну, типа, если я раскрою свои секреты - меня выкинут первым при сокращении. Логика понятная, но грустная. Попытки улучшить общие процессы встречали холодно. "Зачем нам это, у нас и так работает". Окей. В итоге приходилось собирать опыт по крупицам из разных источников. Выдумывать решения самому. Делать инструменты в тишине, а потом показывать уже готовое. Вроде ведь наоборот должно быть? Делиться опытом, учиться друг у друга, строить что-то совместно. Но нет. Если бы люди делились наработками - я бы запилил этот фреймворк на пару лет раньше. Точно. С другой стороны - может, и хорошо, что пришлось делать с нуля. Хотя бы теперь хочется делиться открыто. Пусть даже через статьи и публикации в интернете. Да, весь этот фреймворк я делал один. Не потому что супергерой, а потому что так сложилось. Рекордер, раннер, систему скоринга, мониторинг - всё по частям, в свободное время. Месяца 3-4 месяца ушло. Зато теперь понимаю каждую строчку кода и знаю, где что лежит. Что я построил (и почему это работает)Идея простая до безобразия: записал действия мышью и клавиатурой в JSON, потом проиграл обратно. Но дьявол, как всегда, в деталях. Вот как это выглядит схематично:
Архитектура в двух словахПроект состоит из двух частей: 1. Рекордер - записывает ваши действия Запускаешь скрипт, жмёшь горячую клавишу - началась запись. Кликаешь мышкой, вводишь текст, жмёшь кнопки. Останавливаешь той же клавишей. Сохраняешь другой. Всё. Фишка в том, что рекордер не просто пишет координаты. Он захватывает метаданные UI-элементов через Windows UI Automation: Это значит, что при воспроизведении раннер не тупо кликает по координатам. Он ищет элемент через UI Automation - сначала по идентификатору, потом по роли и имени, потом по пути в дереве интерфейса. Координаты - только запасной вариант. 2. Раннер - воспроизводит и проверяет Раннер берёт JSON-файлы из указанной папки, последовательно их выполняет и собирает отчёт в HTML. Вот что происходит при запуске теста:
JSON-сценарии - это простоВот минимальный пример теста: Читается как документация. Не нужен программист, чтобы понять, что тут происходит. Почему Windows и Linux?Windows - потому что там работает 90% нашего софта. UI Automation - родная технология, работает отлично через системную библиотеку. Linux - потому что хотелось. Сделал базовую поддержку, но без UI Automation там всё хуже. Пока в статусе "работает, но не для прода". Практические нюансы (где я набил шишки)Медленные окна и нестабильность UIОфисные приложения и файловые менеджеры любят "думать". Открываешь окно, а оно ещё 2 секунды дорисовывается. Кликаешь по кнопке, а UI-дерево меняется прямо во время клика. Решение: таймауты и стабилизация.
Звучит как костыль? Да. Но работает. Неожиданные диалогиОткрываешь файл, а там вылезает диалог "Включить макросы?" или "Обновление доступно". Тест падает, потому что не ожидал этого окна. Решение: паттерны вместо точных строк в заголовках окон. Этого хватает, чтобы найти окно, даже если заголовок содержит путь к файлу или версию приложения. Проблемы с фокусомWindows - странная штука. Иногда окно "как бы" в фокусе, но клики не работают. Или фокус улетает на другой монитор (привет, RDP). Решение: явная установка фокуса через системную библиотеку автоматизации. Плюс сохраняем идентификатор процесса, чтобы при повторном фокусе точно попасть в нужное окно. Умный поиск оконЗнаете, что меня удивило? Окна не ищутся по принципу "всё или ничего". Раннер умеет находить "наиболее похожее" окно. Реальность грязная. Заголовок окна постоянно меняется. То путь к файлу дописывается, то версия приложения. То локализацию поменяли. Если тупо требовать 100% совпадение - тесты падают каждый второй запуск. Поэтому есть эвристики. Раннер смотрит на несколько признаков окна сразу. Какие-то важнее, какие-то менее. Выбирает окно, которое "больше всего похоже" на то, что записывали. Пример. Записали тест для "Документ1.txt - Редактор". Открыли "Документ2.txt - Редактор". Тест находит окно. Потому что достаточно признаков совпало - это то же приложение, та же структура заголовка. Или другое. Окно называлось "Settings", стало "Настройки" после смены языка. Ок, заголовок не совпал. Но другие характеристики окна те же - раннер понимает, что это оно. Это не магия. Иногда всё равно приходится обновлять метаданные. Но количество "упал непонятно почему" снижается заметно. Тест стал терпимее к мелким изменениям. Ввод текста - это отдельный квестТри способа ввести текст:
Решение: умный режим. Раннер пробует методы по порядку: системные события → буфер → эмуляция. Первый, что сработал - и ок. Что упрощает JSON-структура
Типы проверок (скриншоты vs UI-ассерты)Сначала я делал только визуальное сравнение. Снял эталонный скриншот, потом сравнил с текущим. Работает, но:
Потом добавил UI-ассерты. Проверяем не "как выглядит", а "что есть". Проверка окнаПроверяет, что окно с нужными параметрами существует: Быстро, стабильно, не зависит от визуальных изменений. Проверка чекбоксаПроверка выбранного значенияБонусы, которые просто работаютАвтопроверка закрытых окон Это мелочь, но приятная. Раннер сам отслеживает, какие окна ты закрывал во время теста. Alt+F4 нажал? Кнопку "Закрыть" кликнул? Запоминает. После выполнения всех шагов автоматически проверяет - а закрылись ли они на самом деле? Не висят ли где-то в фоне? Знаете, сколько раз у меня тесты "проходили", а потом оказывалось, что приложение осталось открытым и жрёт память? Теперь это ловится автоматом. Проверки файлов Не только UI. Можно проверить файловую систему. "Этот файл должен появиться после теста". "Этот должен измениться". "А этот - остаться без изменений". Для тестов типа "Сохранить документ", "Экспортировать отчёт" - незаменимо. Раннер снимает снимок файла до действий (хеш, размер, время модификации), потом проверяет после. Пример из жизни. Тест "Сохранить как PDF". Кликаем кнопки, вводим путь. Тест проходит. А PDF не создался - диалог закрылся, но сохранение упало молча. Раньше это находили только вручную. Теперь тест сам скажет "файл не появился". Fallback на координаты Помните, я говорил про умный поиск через UI Automation? Так вот, если он не сработал - раннер откатывается на координаты. То есть тест не падает сразу намертво. Сначала пытается найти элемент "правильно", не получилось - кликает туда, куда записали при создании теста. Да, координаты хрупкие. Да, могут не попасть. Но это лучше, чем сразу упасть с ошибкой "элемент не найден". Иногда это спасает. Захват состояния без клика В рекордере есть фича. Нужно зафиксировать состояние чекбокса, но не кликать по нему? Наводишь курсор, жмёшь специальную клавишу - рекордер запоминает состояние элемента под курсором. Потом раннер проверит: "чекбокс должен быть включен, как записали". Без лишних действий. Раньше приходилось костылить - кликать, снова кликать, чтобы вернуть в исходное состояние. Теперь просто навёл и зафиксировал. Для 80% тестов этого достаточно. Скриншоты оставил для случаев, когда нужно проверить внешний вид (дизайн, графики, кастомные контролы). Мониторинг и отчётыБонусом прикрутил мониторинг системы. Во время выполнения тестов раннер собирает:
Всё это попадает в HTML-отчёт. Удобно, когда тест упал не из-за бага, а потому что машина тормозила. Отчёты формируются автоматически: таблица с результатами (зелёный/красный), скриншоты при фейлах, дифф-изображения, логи. Открываешь в браузере и сразу видно, что сломалось. Где это может быть полезноЯ делал это для себя, но понял, что подходит для:
Бизнес-эффекты (почему это важно компаниям)Окей, давайте про деньги и время. Потому что начальству обычно нужны цифры, а не "мне нравится этот подход". Ручной регресс съедает времяНа прошлом проекте у нас был образ виртуального рабочего места. Десяток приложений, которые надо проверить перед релизом. Ручная проверка занимала у одного QA примерно 2 недели. Два человека - месяц работы на регресс. Каждый релиз. С автотестами это сократилось до 3-4 дней. Где-то 60-70% экономии времени. Не идеал, но уже можно дышать. И это реальные цифры, не из презентации для инвесторов. QA перестают быть узким местомЗнаете, что бесит руководителей проектов? Когда тесты пишет один автоматизатор на всю команду. Он становится бутылочным горлышком. Заболел, ушёл в отпуск, уволился - всё, автотесты стоят. Тут другая история. QA-инженер записывает тесты сам. Без кода. Нажал кнопки, сохранил JSON, готово. Порог входа низкий. Не нужно учить Python полгода. Это разгружает автоматизаторов и даёт QA больше независимости. Разработчики тратят меньше времени на поддержкуУ нас был случай. Обновили интерфейс - поменяли расположение кнопок. Selenium-тесты полегли пачкой. Автоматизатор две недели чинил XPath и CSS-селекторы. Две недели! С этим фреймворком быстрее. Элементы ищутся через UI Automation - по роли, имени, идентификатору. Если кнопка "Сохранить" переехала на другое место, но осталась кнопкой "Сохранить" - тест найдёт её. Иногда вообще ничего править не нужно. Иногда - минут 10 на пачку тестов. Thick-клиенты больше не "чёрная дыра"Раньше толстые приложения либо не тестировали автоматически вообще, либо пытались костылями типа Sikuli. Скриншоты, поиск по картинкам - это всё работает через раз и ломается от малейших изменений. Здесь автоматизация через системный UI Automation. Тот же подход, что в Selenium, но для десктопа. Это открывает автоматизацию для целого пласта приложений, которые раньше были "вне зоны доступа". Офисные пакеты, CAD, банковские клиенты, всякие SCADA. Релизы идут быстрееПростая математика. Раньше: регресс 2 недели → блокирует релиз → релиз раз в месяц, если повезёт. Теперь: регресс 3 дня → можно выпускать каждую неделю. Или чаще. На практике видел ускорение в 2-3 раза точно. Где-то доходило до 5 раз, но там много факторов. Зависит от того, сколько у вас вообще ручных проверок было. Меньше "флакающих" тестовЗнаете эту боль? Тест то падает, то проходит. Без причины. Проверили вручную - всё работает. Перезапустили тест - прошёл. Это выматывает. Команда перестаёт доверять автотестам. Тут стабильнее, потому что поиск через UI Automation менее хрупкий, чем клики по координатам или по сложным XPath. Не идеально, конечно. Бывают зависания окон, медленные приложения. Но в целом процент ложных падений ниже. Импортозамещение и кроссплатформенностьЕсли у вас проект с российским софтом или переход на него - это больная тема. Многие инструменты заточены под западные продукты. А тут можно тестировать отечественные офисные пакеты, операционки, что угодно. Работает на Windows, есть базовая поддержка Linux. Для компаний, которые строят решения на импортозамещении - это не просто удобно, это критично. Потому что альтернатив мало. VDI и виртуальные рабочие местаАвтотесты можно гонять прямо внутри виртуальных машин и VDI-сред. Проверили образ, зафиксировали результат. Не нужно разворачивать тестовые стенды на физических машинах. Всё внутри виртуалки. Для компаний с большими парками виртуальных рабочих мест (банки, госсектор, корпорации) - это экономия инфраструктуры и времени на подготовку окружения. Итого: считаем выгодуДавайте грубо прикинем. Команда из 10 QA. Половина времени - ручной регресс. Средняя зарплата - ну, сами знаете. Экономия 50-70% времени на регресс = можно либо меньше людей нанимать, либо те же люди делают больше полезной работы (исследовательское тестирование, улучшение процессов). Плюс ускорение релизов. Быстрее релизы - быстрее фичи до пользователей - быстрее обратная связь. Это уже влияет на продукт. Плюс снижение рисков. Меньше багов доходит до прода, потому что регресс теперь гоняется чаще и стабильнее. Всё это конвертируется в деньги. Не сразу, но конвертируется. Демо: как это выглядитЗапись теста
Файл сохраняется в JSON, можно сразу запускать. ВоспроизведениеРаннер собирает все JSON из указанной папки, выполняет по порядку, формирует отчёт. Пример логовВсё прозрачно. Если упало - видно на каком шаге. Что можно улучшить (и куда расти)Сейчас фреймворк работает, но есть идеи, как сделать его умнее. Вот как AI может помочь:
AI-автогенерация тестовПредставьте: запускаете приложение, говорите "создай тест, который откроет файл и сохранит его". AI анализирует UI-дерево, находит нужные элементы, генерирует JSON. Уже экспериментирую с мультимодальными моделями для распознавания интерфейсов. Самовосстановление при изменении интерфейсаТест упал, потому что кнопка переехала? AI может попробовать найти её по похожему имени или роли, обновить метаданные и продолжить. Это снизит количество ложных падений. Работа с "чёрными ящиками"Есть приложения, которые рендерят UI в Canvas или Direct3D. UI Automation их не видит. Тут нужны OCR + компьютерное зрение. Можно натравить модель на скриншот и спросить: "где тут кнопка Сохранить?" Получить координаты, кликнуть. Уменьшение ручного регрессаМечта: записать 500 тестов один раз, потом гонять их каждый билд. Регресс из недель превращается в часы. Уже работает, но нужно масштабировать. Выводы (или почему это не серебряная пуля)Я не хочу продавать вам идею, что это решает все проблемы. Нет. Минусы:
Плюсы:
Это не замена Selenium. Это инструмент для другой задачи - автоматизации десктопных приложений, которые игнорируются в мире автотестов. |