惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
A
About on SuperTechFans
Apple Machine Learning Research
Apple Machine Learning Research
The Register - Security
The Register - Security
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
博客园_首页
U
Unit 42
T
Tailwind CSS Blog
G
GRAHAM CLULEY
F
Full Disclosure
V
Vulnerabilities – Threatpost
T
Tenable Blog
月光博客
月光博客
P
Privacy & Cybersecurity Law Blog
P
Privacy International News Feed
K
Kaspersky official blog
Scott Helme
Scott Helme
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News and Events Feed by Topic
T
The Exploit Database - CXSecurity.com
N
News and Events Feed by Topic
有赞技术团队
有赞技术团队
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LINUX DO - 最新话题
Recorded Future
Recorded Future
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
The GitHub Blog
The GitHub Blog
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
P
Proofpoint News Feed
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
罗磊的独立博客
S
Security Affairs
M
MIT News - Artificial intelligence
L
LINUX DO - 热门话题
美团技术团队
Simon Willison's Weblog
Simon Willison's Weblog
T
Threat Research - Cisco Blogs
Stack Overflow Blog
Stack Overflow Blog
Forbes - Security
Forbes - Security
Hugging Face - Blog
Hugging Face - Blog
博客园 - Franky
V
Visual Studio Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как я сделал утилиту для автоматизации ручных тестов
Alexey42o · 2026-05-22 · via Все публикации подряд на Хабре

Как я сделал утилиту для автоматизации ручных тестов

Уровень сложностиПростой

Время на прочтение15 мин

Охват и читатели16

Обзор

Привет, меня зовут Алексей и я C# разработчик. Однажды передо мной стояла задача написать утилиту для взаимодействия с различными UI-элементами в Windows и во всех популярных браузерах. Сама утилита не была связана с тестированием, но вполне годилась для автоматизации некоторых действий на машине, так как была простой в управлении и интуитивно понятной. Мне понравилось работать в этом направлении и возникла идея создания инструмента, который не будет перегружен широким функционалом RPA решений, но возьмёт от них всё что нужно для тестирования интерфейсов, чтобы получился действительно полезный инструмент-помощник для QA с низким порогом входа. Назвал я его RTHelper.

Я хочу рассказать в этой статье о нескольких вещах:

  • как превратить элемент интерфейса в структуру, по которой мы его потом однозначно найдём

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

  • как выстраивать стабильную цепочку действий с UI, которая не падает если элемент переместился или грузился на 3 секунды дольше

  • как быть если интерфейс переменчив и нам нужно получать подробный отчёт об этих изменениях

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

Введение

В UI-тестировании есть неприятная зона между ручной проверкой и полноценной автоматизацией. Руками делать долго и скучно, а полноценная автоматизация всё ещё требует кода, времени и поддержки. Особенно в регрессах где нужно просто повторять типовые действия: открыть окно, нажать кнопки, ввести данные, проверить текст, снять результат. Я хотел закрыть именно этот промежуток: сделать инструмент, который позволяет собирать и воспроизводить UI-сценарии без кода, но при этом не превращается в тяжёлую RPA-систему. По сравнению с фреймворками типа Playwright под управлением ИИ, визуальный конструктор с полным инструментарием под рукой должен стать удобнее когда сценарий или интерфейс сложные и алгоритм надо поддерживать, особенно когда надо править и пробовать локаторы.

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

Стандартный inspect.exe - пример того как строится UI дерево и какими свойствами обладают элементы

Стандартный inspect.exe - пример того как строится UI дерево и какими свойствами обладают элементы

Далее, развивая функционал, я добавил в свою утилиту возможности для взаимодействия с сохранёнными элементами: клик, скролл, drag and drop, что угодно… А также работа с сущностями самих приложений: запуск с параметрами, свернуть, эмуляция ввода с клавиатуры…

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

  • Проигрыватель алгоритмов, состоящих из шагов типа “элемент+действие”

  • Набор действий направленных на стабилизацию алгоритма (Wait, скриншот-проверки и другое)

  • Инструмент для анализа результатов запуска с подробным отчётом

  • Ну и конечно соответствующий интерфейс с редактором параметров, деревом алгоритма и т.д.

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

Работа с UI-элементами изнутри

Я решил начать с простого и реализовать два режима работы с UI: windows и web. Другие платформы пока в планах на развитие, а что касается этих, то я пришёл к тому чтобы использовать такие подходы:

  • Для windows - UIAutomation, можно сказать целевое API для взаимодействия с UI от майкрософт, наследник MSAA

  • Для web - после долгих проб и ошибок я выбрал написать собственное браузерное расширение с целью иметь полную власть над веб-интерфейсами

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

  1. Максимально широкий список доступных действий с элементами, как в больших RPA-решениях

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

  3. Возможность находить элементы по набору атрибутов который задаст пользователь

  4. Возможности по управлению самим приложением: запуск, ресайз, переключение вкладок, закрытие и прочее

  5. И конечно же надёжность и стабильность, потому что сценарий тестирования не должен падать по вине самой утилиты

Работа с UIAutomation (UIA режим)

UIAutomation - это библиотека, на которой построены практически все решения и фреймворки в области автоматизации UI для windows. Много документации, работать с ней несложно, особенно если вникнуть в то как вообще строится и затем обходится дерево элементов в интерфейсе, однако магии ждать от неё тоже не стоит. Мы не можем исключить следующие сложности:

  • Отсутствие у элемента надёжных атрибутов для идентификации

  • Наличие множества элементов с одинаковыми атрибутами

  • Множество окон у одного приложения, имеющих одинаковые свойства и имена процесса

  • Устаревшие интерфейсы (например на c++/delphi), в которых интерфейс иногда строится вообще непредсказуемо

  • Элемент попросту отсутствует в интерфейсе / не прогрузился

К сожалению, как бы я ни хотел сделать для пользователя всё магически рабочим, ответственность за надёжность и стабильность в перечисленных случаях ложится на него. Но я могу предложить варианты и реальные практики для решения таких проблем:

  • Использовать область поиска для элемента (сначала ищем стабильный элемент, а потом среди его дочерних)

  • Иногда можно использовать MatchIndex/MatchReverse (поиск по индексу среди подходящих)

  • Использовать скриншот-тестирование, в частности можно записать эталон самого элемента и сравнивать только его

  • Использовать умное ожидание элементов, wait’ы (мы пробуем найти элемент на экране пока не наступит таймаут)

  • Якориться на соседние или связанные элементы и действовать от них (в совсем крайних случаях можно программно перемещать курсор или кликать по координатам)

  • Не забывать про хоткеи в программах и взаимодействовать через них (в моей реализации есть для этого SendKeys)

  • В веб режиме можно выполнить JS скрипт прямо в странице, для win-программ обращаться через API если вдруг есть такая возможность

Чтобы удобно применять всё вышеперечисленное я написал библиотеку для взаимодействия с UI, которая максимально абстрагирует нас от прямой работы с данными об элементах и сводит алгоритм к примерно такому коду:

Скрытый текст
service.Wait(element_attributes);
var webElement = service.Find<WebElement>(element_attributes);
webElement.Click();

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

Работа с браузерами (Web режим)

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

При первом же столкновении с реальностью стало понятно что мне не хватает возможностей и всё сводилось к вызову скриптов, не было возможности (по крайней мере в тот момент) работать с окнами, вкладками и прочее. Я перешёл к более низкоуровневому подходу: написал своё браузерное расширение с использованием native messaging - это считается целевым решением когда необходимо обмениваться сообщениями между десктопным приложением и расширением и про это у меня есть отдельная статья с гайдом по настройке.

Примерное количество атрибутов у веб элемента

Примерное количество атрибутов у веб элемента

Но и это мне не подошло, так что я перешёл к веб сокетам, потому что сама идея native messaging мне не подходила и дестабилизировала работу. Основные причины: общение через консоль и таскание в дистрибутиве лишнего exe, для которого требовалась лишняя настройка окружения. Так что я не вижу ничего плохого в том чтобы обмениваться данными через WS на localhost, тем более мы получаем возможность легко придерживаться своих понятных DTO и имеем максимальный доступ ко всему что происходит с браузером.

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

Нюансы поиска элементов

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

Скрытый текст
public class ElementDescriptor
{
    public string Name { get; set; }
    public CaptureType CaptureType { get; set; }
    public string? ElementType { get; set; }
    public List<Property> Properties { get; set; }
    public SearchScopeDescriptor? SearchScope { get; set; }
}

Одни свойства полезны только для информации, другие слишком переменчивы, а есть те, которые хорошо подходят именно для поиска. Например, в win-режиме можно получить AutomationId, Name, ClassName, ControlType, положение элемента, состояние доступности, данные родительского окна и процесса. В идеальном мире для поиска хватило бы AutomationId, потому что это стабильный технический идентификатор. В реальности он часто пустой, повторяется или отсутствует в старых и кастомных интерфейсах. Тогда приходится комбинировать несколько признаков: тип контрола, имя, родительское окно, иногда соседние или родительские элементы.

В Web-режиме история похожая, только у нас DOM и при захвате элемента можно собрать его id, name, class, tag, type, role, текст, набор aria-* атрибутов, XPath, CSS-подобные признаки, положение в дереве и другие свойства. Но здесь тоже нет одного идеального ответа. id может генерироваться фреймворком, классы могут быть хэшами, текст может зависеть от локали или тестовых данных, а XPath может развалиться после небольшого изменения верстки.

Поэтому я бы разделил атрибуты на несколько групп:

  • стабильные технические признаки: AutomationId, id,role, ControlType, tag

  • человеческие признаки: текст кнопки, подпись поля, Name

  • контекстные признаки: окно, процесс, вкладка, родительский контейнер, форма, таблица

  • структурные признаки: XPath, позиция среди похожих элементов, индекс совпадения

  • служебные признаки: координаты, размеры, состояние видимости, enabled/disabled

Лучше всего работают первые три группы. Структурные признаки полезны когда других вариантов нет, а координаты и размеры я бы вообще не делал основой поиска: они пригодятся только для fallback-сценариев.

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

  1. Внутри сам процесс поиска получается двухфазным: сначала берём все свойства, которые пользователь отметил как значимые и пытаемся получить список кандидатов, а затем, если кандидатов несколько, применяем дополнительные фильтры: регулярные выражения, wildcard-сравнение, ограничения по родителю, область поиска, индекс совпадения.

  2. Когда остаётся несколько похожих элементов, можно использовать MatchIndex и MatchReverse: выбрать первый, второй или последний элемент среди найденных. Это полезный механизм, но я считаю его компромиссом, ведь если порядок строк в таблице зависит от сортировки или данных, то индекс легко станет причиной падений.

  3. Рекомендую активно пользоваться подсветкой найденных элементов: после настройки атрибутов можно проверить, что программа считает подходящим элементом. Если подсветилось пять похожих кнопок - локатор слишком широкий, а если не подсветилось ничего - мы выбрали слишком строгие признаки или элемент ещё не появился.

  4. Иногда нужно ограничивать область поиска: сначала заякориться на модальном окне, таблице или форме, а уже внутри искать кнопку, поле или строку. В интерфейсе это делается в два клика и одно перетаскивание.

Наглядно - как влияют на результат выбранные параметры

Наглядно - как влияют на результат выбранные параметры

Нюансы управления элементами

После того как элемент найден всё тоже не так просто: "кликнуть" можно разными способами, а ввод текста через клавиатуру и установка значения напрямую - это не одно и то же. Нажатие кнопки, ввод текста, выбор значения из списка или чтение состояния могут выполняться разными способами в зависимости от того, Web это или Windows, какой тип контрола перед нами и какие функции он поддерживает.

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

Скрытый текст
public class Step
{
    public ElementDescriptor Element { get; set; }
    public string TypeName { get; set; }
    public string ActionName { get; set; }
    public List<MethodParameter> Parameters { get; set; }
    public int AutoWaitMs { get; set; }
    public AutoWaitOptions AutoWaitOptions { get; set; }
    public string ResultVariable { get; set; }
}

В win-режиме часть действий можно выполнять через UIAutomation patterns: например, InvokePattern для кнопок, ValuePattern для полей ввода, SelectionItemPattern для элементов списков. Если кнопка переехала на 20 пикселей, логическое нажатие всё равно должно сработать, но UIA паттерны доступны не всегда. Некоторые контролы не реализуют нужный паттерн, некоторые отдают странные состояния, а некоторые старые интерфейсы вообще не работают с UIAutomation. Поэтому приходится иметь запасные варианты: Win32-вызовы, SendKeys, иногда координатные действия, но я стараюсь относиться к этому как к fallback, а не основному способу управления.

В Web-режиме похожая логика. Если нужно нажать кнопку, можно вызвать DOM-событие или использовать браузерный API. Если нужно записать значение в input, мало просто поменять value: современные фронтенд-фреймворки часто ждут цепочку событий input, change, иногда blur. Поэтому действие должно не только изменить свойство элемента, но и сделать это так, чтобы страница восприняла изменение как пользовательский ввод.

Перед выполнением команды полезно убедиться, что элемент действительно готов к действию. Для этого я разделяю обычное ожидание и готовность элемента. У меня в программе реализован настраиваемый Auto-wait перед шагом, который смотрит что: элемент найден, видим, доступен, не перекрыт другим элементом, перестал двигаться, прокручен в область видимости. Это защита от ситуации, когда сценарий пытается нажать слишком рано.

В Web-режиме особенно важны проверки "visible", "enabled", "uncovered" и "stable". В Windows-приложениях картина похожая: окно может уже существовать, но контрол ещё не готов принимать действие.

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

Такой подход не убирает всю сложность UI-автоматизации, но пользователь видит понятный шаг "нажать кнопку" или "ввести текст", а внутри библиотека выбирает, как именно это сделать для Windows или браузера. И если что-то падает, видно причину: элемент не найден/не тот, команда не поддерживается или результат не совпал с ожидаемым. И чтобы ещё сильнее компенсировать сложности я добавил режим отладки, чтобы пошагово следить за выполнением.

Пример создания элементарного алгоритма

Пример создания элементарного алгоритма

Как сценарий стал тестом

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

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

Самый простой пример - авторизация. Если сценарий ввёл логин, пароль и нажал "Войти", это ещё не тест. Возможно, кнопка не сработала, форма показала ошибку, пользователь остался на той же странице, но сам клик технически прошёл успешно. Тест обычно выгдядит так: после входа должен появиться такой-то элемент, текст должен быть таким-то, API должен вернуть статус 200, а блок профиля должен визуально соответствовать эталону.

Wait: ждать не время, а состояние

Первое, что нужно почти любому UI-сценарию, - нормальные ожидания. Не sleep(3000), а ожидание конкретного состояния. Поэтому отдельный шаг Wait я воспринимаю не как паузу, а как часть смысла сценария. Он отвечает на вопрос: "какое состояние приложения должно наступить, прежде чем мы пойдём дальше?". Например:

  • элемент появился

  • элемент исчез

  • текст изменился

  • окно стало доступно

  • значение стало равно ожидаемому

В ручном тест-кейсе мы тоже пишем не "подождать 2 секунды", а "дождаться появления сообщения об успешном сохранении".

Ожидание загрузки после запуска

Ожидание загрузки после запуска

Assert: проверять и сравнивать результат

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

Также полезны разные типы сравнения: точное равенство, "содержит", регулярное выражение, больше/меньше и прочее. На практике UI часто возвращает текст с пробелами, префиксами, динамическими частями. Поэтому строгая проверка нужна не всегда, иногда надёжнее проверять именно значимую часть результата.

Пример того, как можно валидировать результат: в самом действии или отдельным шагом

Пример того, как можно валидировать результат: в самом действии или отдельным шагом

По поводу переменных: если сценарий начинает жить дольше одного запуска, в нём появляются логины, номера заявок, пути к файлам, ожидаемые статусы. Должна быть возможность переиспользовать и загружать такие данные, для этого я добавил переменные и их загрузку из файлов. С LoadData можно вынести пары "имя переменной - значение" в CSV, XLS или XLSX и подставлять их в шаги, это уже достаточно сильно снижает дублирование.

Визуальные и API проверки

Есть случаи, когда результат невозможно валидировать через обычные проверки атрибутов и текста. Например, съехала верстка, кнопка стала неактивной визуально или поменялся цвет. Для таких случаев я добавил скриншот-тестирование.

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

Пример успешного и неуспешного сценариев тестирования

Пример успешного и неуспешного сценариев тестирования

Также иногда есть необходимость отправить HTTP-запрос, чтобы проверить статус и другие значения из ответа. Для этого я добавил возможность работы с API, оставив в быстром доступе проверки возвращаемых значений и снятие временных метрик. Примерный сценарий когда это может пригодиться выглядит так:

  1. Создать объект через интерфейс

  2. Прочитать его номер из сообщения на экране

  3. Вызвать API по этому номеру

  4. Проверить статус и поля в JSON

  5. Вернуться в UI и убедиться, что объект отображается в списке

Пример проверки API-запроса

Пример проверки API-запроса

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

Отчётность и результаты тестов

Когда сценарий падает, нам мало знать "не прошёл шаг 17", ведь нужно понять: что это был за шаг, какой элемент искался, сколько времени он ждал, какой результат вернул и в чём конкретно заключалась ошибка. Поэтому вокруг выполнения сценария появились отладка, журнал шагов, история запусков и скриншот при падении.

Для регулярных запусков важны отчёты не только в интерфейсе программы, но и в формате, который можно передать дальше: JSON, JUnit, Allure. Тогда визуально собранный сценарий становится ближе к обычному тестовому прогону: его можно запускать, анализировать, хранить историю и подключать к процессу команды.

Пример создания набора сценариев

Пример создания набора сценариев

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

Куда дальше

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

  1. Более умная работа с локаторами. Сейчас инструмент подсказывает какие атрибуты нестабильны и много берёт на себя, но хочется совсем освободить пользователя от нюансов работы с UI и скрыть эти настройки. В идеале сделать целевой кнопку "Record", которая просто запишет все ручные манипуляции с интерфейсом за пользователем.

  2. Улучшение анализа падений. Я определённо хочу доработать визуализацию истории запусков и сделать для пользователя каждый прогон предельно информативным и понятным. Стремлюсь к тому чтобы шаг сценария содержал максимум данных о том что он делает или сделал.

  3. Больше платформ и типов проверок. Сейчас основной фокус у меня на Windows и Web, но подход с дескрипторами, ожиданиями и прочим можно переносить дальше. Очень хочу и планирую совместить программу с тестированием мобильных устройств.

  4. Интеграция с привычными процессами. Пользователь не должен быть заперт внутри программы, я уже постарался дать экспорт в код, который может быть интересен для разработчиков. Плюс отчёты в JUnit, Allure и JSON, которые важны как мост к CI и командной разработке. Сейчас я активно дорабатываю это направление, но требуется больше обратной связи.

Заключение

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

Если вы захотите попробовать RTHelper (нет обязательной регистрации), то можно начать с самого простого: открыть форму, заполнить поля, нажать кнопку, проверить результат. Если его получится довести хотя бы до нескольких стабильных запусков подряд, сразу станет понятно, где инструмент экономит время, а где чего-то не хватает.

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