Введение
Автоматизированное регрессионное тестирование графического интерфейса мобильных приложений приобретает особую значимость по мере роста сложности пользовательских сценариев и частоты выпуска обновлений на платформе Android [1-3]. Ошибки взаимодействия и отображения нередко обусловлены динамическим состоянием приложения и проявляются исключительно на уровне пользовательского интерфейса [4; 5], что повышает требования к точности и устойчивости средств автоматизации. Поставка приложений в виде упакованных артефактов формата APK дополнительно усложняет задачу: исходный код тестируемого продукта зачастую недоступен либо его модификация недопустима по организационным или юридическим причинам, а формат APK, в отличие от настольных бинарных артефактов, дополнительно регламентирован требованиями подписи и метаданных уровня операционной системы.
Существующие подходы к автоматизации тестирования Android-приложений включают визуальное распознавание элементов интерфейса, средства уровня операционной системы и решения, опирающиеся на интерфейсы доступности или скриншоты [6-8]. Внешние подходы зависят от внешнего представления интерфейса и характеризуются недостаточной точностью идентификации элементов в условиях эволюции графического интерфейса [9-11]. Решения, требующие интеграции в проект тестируемого приложения, противоречат сценарию работы с готовым APK без доступа к исходному коду [12-14].
В ранее опубликованной работе авторами был сформулирован метод неинвазивного автоматизированного тестирования графического интерфейса настольных Qt-приложений [15]. Данный метод был рассмотрен применительно к настольным операционным системам, где внедрение тестового агента может выполняться с использованием штатных механизмов предзагрузки или принудительной загрузки разделяемых библиотек. На платформе Android такие механизмы не имеют прямого аналога, поскольку приложение поставляется в виде APK-пакета, а его компоненты, разрешения и параметры запуска фиксируются в манифесте AndroidManifest.xml – декларативном описании приложения. Поэтому перенос подхода на Android требует решения трёх связанных задач: внедрения тестового агента в адресное пространство процесса приложения без модификации исходного кода и декларативного описания приложения; организации канала связи между агентом и внешней системой тестирования без добавления новых Android-разрешений; передачи параметров сеанса агенту в условиях отсутствия привычной для настольных приложений командной строки и управляемого набора переменных окружения процесса.
Под неинвазивностью в настоящей работе понимается сохранение публичного контракта приложения (имени пакета, набора компонентов, заявленных разрешений) и семантики его поведения. Соответствие этому требованию принципиально для применимости метода к Android-приложениям рассматриваемого класса, поставляемым в виде готового APK, поскольку нарушение хотя бы одного из элементов публичного контракта может привести к расхождению наблюдаемого поведения с поведением исходного приложения.
Цель исследования – расширение приёма динамической инъекции кода на платформу Android при сохранении неинвазивности: внедрение тестового агента в процесс приложения, организация канала связи с внешней системой тестирования и передача параметров сеанса без правки исходного кода и манифеста.
Материалы и методы исследования
Объектом исследования являются Android-приложения с графическим интерфейсом, реализованным на основе фреймворка Qt (Qt Widgets либо Qt Quick/QML), поставляемые в виде упакованных артефактов формата APK. Выбор Qt обусловлен распространённостью этого фреймворка для создания кроссплатформенных приложений и развитой объектной моделью интерфейса [15].
Методологическая основа исследования включала анализ документации Android NDK и инструментов сборки в части механизмов динамической загрузки разделяемых библиотек и формата APK, изучение механизма проброса соединений средствами ADB, проектирование архитектуры метода и его прикладную апробацию. Апробация метода проводилась на наборе тестовых Android-приложений, разработанных с использованием Qt 5.15, 6.5 и 6.8. В состав набора входили приложения, реализующие графический интерфейс с применением Qt Widgets и Qt Quick/QML. Экспериментальная проверка была направлена на подтверждение применимости метода к различным версиям Qt и двум основным технологиям построения интерфейса в рамках единой схемы внедрения тестового агента. Проверка выполнялась на эмуляторе Android.
Результаты исследования и их обсуждение
На платформе Android подготовка к динамической инъекции кода выполняется на этапе формирования тестового экземпляра APK и заключается в дополнении его файловой структуры тестовым агентом без изменения логики самого приложения, а непосредственная загрузка агента происходит с помощью штатного механизма динамической компоновки Android. По аналогии с механизмами предзагрузки разделяемых библиотек в Linux и macOS [15] предлагаемый подход использует штатный механизм разрешения зависимостей при загрузке нативных библиотек.
Подготовительный этап включает анализ исходного APK как контейнера приложения: определяется целевая архитектура процессора, состав размещённых в пакете нативных разделяемых библиотек и версия фреймворка Qt, с которой было собрано тестируемое приложение. Получение мажорной и минорной версий Qt необходимо для выбора сборки тестового агента, совместимой с приложением на уровне бинарного интерфейса: несовпадение версии может привести к ошибкам загрузки библиотеки агента либо к некорректной работе при обращении к объектной модели интерфейса. Мажорная версия определяется по именам базовых библиотек Qt, а минорная – по метаданным, содержащимся в нативной библиотеке ядра Qt.
После выбора совместимого агента формируется временный тестовый экземпляр APK: архив приложения распаковывается, в область нативных библиотек выбранной архитектуры копируется библиотека агента и только отсутствующие зависимости, необходимые для её загрузки, а в динамическую секцию основной нативной библиотеки приложения с помощью patchelf1 добавляется запись DT_NEEDED с именем библиотеки агента. Затем тестовый экземпляр APK заново упаковывается без служебных данных исходной подписи, выравнивается утилитой zipalign2 в соответствии с требованиями Android и подписывается тестовым ключом с помощью apksigner3. Принципиальной особенностью является то, что файл AndroidManifest.xml на этой цепочке не модифицируется: имя пакета, набор компонентов приложения, объявленные разрешения и иные элементы публичного контракта остаются неизменными. В обобщённом виде алгоритм формирования тестового экземпляра APK представлен в листинге 1.
Из перечисленных операций единственная содержательная модификация бинарных артефактов – добавление одной записи DT_NEEDED в динамическую секцию основной разделяемой библиотеки приложения. Машинный код основной нативной библиотеки не изменяется: преобразование затрагивает только её служебную динамическую секцию, в которую добавляется запись о зависимости от агента. Остальные прикладные компоненты исходного APK не модифицируются; из тестовой копии исключаются только служебные данные исходной подписи, поскольку после переупаковки они становятся недействительными. Механизм добавления записи DT_NEEDED по назначению сопоставим с предзагрузкой через LD_PRELOAD в Linux: в обоих случаях обеспечивается загрузка дополнительной разделяемой библиотеки в адресное пространство процесса при запуске приложения. В результате агент загружается на раннем этапе инициализации нативной части приложения, до начала основной работы Qt-приложения, что создаёт условия для регистрации хуков объектной модели интерфейса.
Установление связи между агентом, работающим внутри процесса Android-приложения, и внешней системой тестирования осложняется тем, что использование сетевых TCP-сокетов на стороне приложения требует объявления разрешения android.permission.INTERNET в манифесте приложения. Добавление такого разрешения изменяло бы публичный контракт приложения и нарушало бы требование неинвазивности. Поэтому в качестве транспортного механизма на стороне устройства используется Unix domain socket в abstract namespace4, то есть локальный сокет семейства AF_UNIX, адресуемый не файловым путём, а именем в пространстве имён ядра. Такой сокет не создаёт объекта в файловой системе и не относится к сетевому семейству AF_INET, вследствие чего его использование не требует ни дополнительных прав доступа к файлам, ни объявления сетевого разрешения Android.
Связь с внешней системой тестирования устанавливается с использованием штатного механизма проброса соединений Android Debug Bridge5.
Входные данные:
A – исходный APK;
B – целевая ABI;
L_agent – разделяемая библиотека тестового агента;
L_main – основная нативная библиотека приложения;
D_agent – множество зависимостей, необходимых для загрузки агента;
D_app – множество нативных библиотек, присутствующих в исходном APK;
K – тестовый ключ подписи.
Выходные данные:
A_test – подписанный тестовый APK.
1. Создать временный рабочий каталог W.
2. Распаковать A в W.
3. Удалить из W служебные данные исходной подписи.
4. Поместить L_agent в каталог нативных библиотек для ABI B.
5. Для каждой зависимости d ∈ D_agent: если d ∉ D_app, то скопировать d из комплекта Qt for Android, использованного при сборке исходного приложения.
6. Добавить в динамическую секцию L_main запись DT_NEEDED с именем L_agent:
patchelf --add-needed name(L_agent) L_main.
7. Упаковать содержимое W в неподписанный APK A_unsigned.
8. Выполнить выравнивание A_unsigned:
zipalign -f 4 A_unsigned A_aligned.
9. Подписать A_aligned тестовым ключом K:
apksigner sign --ks K --out A_test A_aligned.
10. Вернуть A_test.
Листинг 1. Алгоритм формирования APK с внедрённым тестовым агентом Примечание: составлено авторами по результатам данного исследования
# Создание локального TCP-порта на управляющей машине
# и перенаправление подключений к abstract socket на Android-устройстве.
adb forward tcp:<host_port> localabstract:<abstract_socket_name>
# Запуск Activity тестируемого приложения.
# Имя abstract socket передаётся через Intent extras, чтобы агент создал и прослушивал точку,
# соответствующую пробросу, заранее настроенному управляющим процессом.
adb shell am start -n <package_name>/<activity_name> \
--es <ipc_name_key> <abstract_socket_name>
Листинг 2. Обобщённая схема проброса соединения и запуска Android-приложения с передачей имени канала связи Примечание: составлено авторами по результатам данного исследования.
// Создание локального Unix domain socket внутри процесса Android-приложения.
sockaddr_un address;
int socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
std::memset(&address, 0, sizeof(address));
address.sun_family = AF_UNIX;
// Первый байт sun_path равен ‘\0’, поэтому имя сокета
// интерпретируется как имя в abstract namespace, а не как путь в ФС.
std::memcpy(address.sun_path + 1, socketName, socketNameLength);
socklen_t addressLength = offsetof(sockaddr_un, sun_path) + 1 + socketNameLength;
// Агент создаёт конечную точку, к которой ADB перенаправит соединение.
bind(socketFd, reinterpret_cast<sockaddr*>(&address), addressLength);
// После listen агент может принять подключение управляющего процесса.
listen(socketFd, backlog);
Листинг 3. Ключевые операции создания Unix domain socket в abstract namespace на стороне агента Примечание: составлено авторами по результатам данного исследования.
На управляющей машине создаётся локальный TCP-порт, а удалённой конечной точкой проброса указывается Unix domain socket в abstract namespace на Android-устройстве. Поскольку механизм adb forward не создаёт такой сокет самостоятельно, он должен быть предварительно создан процессом, работающим в Android-среде. В предлагаемом методе эту функцию выполняет тестовый агент, находящийся внутри процесса приложения: он создаёт и прослушивает именованный abstract socket, после чего управляющий процесс подключается к локальному TCP-порту на хост-машине, перенаправленному средствами ADB к этому сокету. После принятия соединения агентом формируется двусторонний IPC-канал, в рамках которого дальнейшее взаимодействие агента и управляющего процесса не зависит от исходного разделения на слушающую и подключающуюся стороны. Такая организация связи не требует повышения привилегий приложения на устройстве и не изменяет набор его Android-разрешений. Обобщённая последовательность настройки канала связи и запуска приложения приведена в листинге 2, а ключевые операции создания abstract socket на стороне агента – в листинге 3.
Передача служебных параметров тестового сеанса осуществляется через механизм Intent extras, то есть через дополнительные пары «ключ – значение», включаемые в объект Intent при запуске Android Activity. В предлагаемой схеме таким образом передаётся, в частности, имя Unix domain socket в abstract namespace, необходимое агенту для создания конечной точки связи с управляющим процессом. Команда adb shell am start поддерживает передачу строковых параметров с помощью аргумента --es, что позволяет задать эти данные на этапе запуска приложения без изменения манифеста и без использования переменных окружения процесса. После загрузки агент считывает переданные значения через интерфейс Java Native Interface, обращаясь к методам объекта Intent, связанного с текущей Activity.
Особенность данного способа состоит в том, что агент может быть загружен раньше, чем Qt-слой Android-приложения завершит привязку текущей Activity к нативному окружению. Поэтому чтение параметров сеанса выполняется не однократно, а с использованием ограниченного числа отложенных попыток с небольшой задержкой между ними. Такая схема учитывает жизненный цикл Qt for Android и не требует синхронизации с пользовательским кодом приложения, что важно для сохранения неинвазивности метода и его применимости к приложениям с различной структурой инициализации.
Поддержание актуального представления объектной модели интерфейса внутри процесса осуществляется тем же способом, что и для настольных приложений [15]: тестовый агент устанавливает обработчики хуков Startup, AddQObject и RemoveQObject через интерфейс qtHookData, сохраняя адреса исходных обработчиков и делегируя им выполнение после собственной обработки. Это сохраняет семантику работы приложения и совместимость с возможным пользовательским кодом, использующим тот же механизм. Адресация элементов интерфейса осуществляется по иерархическому пути в объектной модели, формируемому по приоритету признаков элемента: заданное имя объекта, иные семантические атрибуты, тип объекта и позиция среди однотипных элементов на текущем уровне иерархии.
Для прикладной апробации предложенный метод был реализован в экспериментальном прототипе системы автоматизированного тестирования Qt-приложений qtiva. В рамках настоящей работы qtiva рассматривается не как предмет самостоятельного анализа, а как экспериментальная реализация предложенного метода, позволяющая проверить его применимость в полном тестовом сеансе. Для каждой конфигурации из описанного выше набора проверялись подготовка тестового APK, запуск приложения на эмуляторе Android, загрузка агента в адресное пространство приложения, передача параметров сеанса, установление IPC-канала и получение сведений об объектной модели графического интерфейса.
На рисунке приведён репрезентативный пример одного из тестовых сеансов для приложения на Qt 5.15 с интерфейсом Qt Widgets: в эмуляторе Android выполняется тестируемое приложение, а в отдельном окне управляющего процесса отображается дерево объектов его интерфейса, полученное агентом изнутри процесса приложения. Наличие такого дерева демонстрирует достижение основного результата апробации: внешний процесс получает сведения о внутренней объектной модели Android-приложения без модификации исходного кода и манифеста. При этом рисунок иллюстрирует один экспериментальный прогон, тогда как проверка работоспособности метода выполнялась для всего набора тестовых приложений.

Отображение дерева объектов интерфейса Android-приложения в управляющей панели при записи тестового сценария Примечание: получено авторами в ходе экспериментальной апробации метода
К ограничениям метода относится необходимость согласования мажорной и минорной версий Qt, использованных при сборке тестируемого приложения и тестового агента. Отдельным ограничением является формирование производного APK, подписанного тестовым ключом: в условиях контролируемого тестового стенда это допустимо, однако для приложений, поведение которых зависит от исходной подписи, требуется дополнительный режим проверки либо отдельное обоснование применимости метода.
Заключение
В результате исследования разработан метод неинвазивного автоматизированного тестирования графического интерфейса Android-приложений на основе динамической инъекции кода. Метод обеспечивает внедрение тестового агента в процесс приложения без модификации исходного кода и манифеста, передачу параметров сеанса через механизм Intent extras и установление связи между агентом и внешней системой тестирования с использованием Unix domain socket в abstract namespace и штатных средств проброса соединений ADB.
Систематизированы операции цепочки переупаковки APK, обеспечивающие добавление тестового агента в адресное пространство процесса приложения через механизм динамического связывания операционной системы. Сформулированы условия неинвазивности, заключающиеся в сохранении публичного контракта приложения и семантики его поведения. Применимость метода подтверждена апробацией на эмуляторе Android для тестовых приложений на Qt Widgets и Qt Quick/QML.
[1] patchelf – A small utility to modify the dynamic linker and RPATH of ELF executables. URL: https://github.com/NixOS/patchelf (дата обращения: 02.04.2026).
[2] Android Developers. zipalign. URL: https://developer.android.com/tools/zipalign (дата обращения: 02.04.2026).
[3] Android Developers. apksigner. URL: https://developer.android.com/tools/apksigner (дата обращения: 02.04.2026).
[4] Linux Programmer’s Manual: unix(7) – sockets for local interprocess communication. URL: https://man7.org/linux/man-pages/man7/unix.7.html (дата обращения: 11.04.2026).
[5] Android Developers. Android Debug Bridge (adb) reference: forward command. URL: https://developer.android.com/tools/adb#forwardports (дата обращения: 16.04.2026).
Конфликт интересов
Финансирование
Библиографическая ссылка
Голдышев Д. М., Фетисов М. В. МЕТОД АВТОМАТИЗИРОВАННОГО ТЕСТИРОВАНИЯ ГРАФИЧЕСКОГО ИНТЕРФЕЙСА ANDROID-ПРИЛОЖЕНИЙ БЕЗ ИЗМЕНЕНИЯ ИСХОДНОГО КОДА С ВНЕДРЕНИЕМ ТЕСТОВОГО АГЕНТА // Современные наукоемкие технологии. 2026. № 6. С. 60-66;URL: https://top-technologies.ru/ru/article/view?id=40817 (дата обращения: 03.07.2026).
DOI: https://doi.org/10.17513/snt.40817



