ФРЕЙМВОРК KASPRESSO. ПРИНЦИПЫ РАБОТЫ ADB SERVER
Мацюк Е.В.
Мацюк Евгений Викторович - эксперт-разработчик, направление: Android, Google, г. Москва
Аннотация: в статье рассматриваются принципы работы AdbServer в рамках фреймворка Kaspresso, предназначенного для написания автотестов под Android.
Ключевые слова: Android, Kaspresso, ADB, AdbServer, Kotlin, Espresso, UI Automator.
В сравнении фреймворков автоматизации [1] плюсом Appium часто указывают возможность исполнения adb команд, в то время как Espresso и UI-automator обделены таким функционалом.
В Kaspresso была произведена попытка это исправить, в результате чего появился AdbServer.
В основе Kaspresso лежат Espresso и UI-automator, значит при переходе с Appium теряеся фича — взаимодействие с девайсом по adb.
Но почему вообще это может понадобиться при прохождении тестов? Помимо стандартных команд, таких как установка приложения, скачивание каких-либо файлов с телефона, а также установки системных настроек, adb умеет еще много всего. Например, в Google сделали такую команду — adb emu [2]. Она поднимает на эмуляторе telnet сервис, который позволяет управлять внутренним состоянием телефона и делать такие нестандартные вещи как:
- установка геопозиции
- симуляция прикосновения к сканеру отпечатка пальца
- нестабильное интернет-соединение
- звонок на телефон
- отправка смс
- и многие другие...
Это все является полной эмуляцией системных вызовов, нет необходимости в приложении пытаться замокать отсутствие интернета, система предоставляет то, как это произойдет на реальном устройстве.
Почему в Appium возможно было это сделать, а на Espresso нет?
Работа с Appium выглядит так:
Рис. 1. Схема работы Appium
1. На телефон устанавливается тестируемое приложение
2. Написанные нами тесты находятся на компьютере
3. Поднимается Selenium
4. Selenium поднимает Appium Server
5. Appium Server устанавливает на телефон Bootstrap
6. Общение с BootStrap происходит по adb
Можно заметить, что приложение просто находится на телефоне и с ним нет никакого прямого взаимодействия. Это говорит о том, что Appium тесты — blackbox.
Что происходит, когда мы пишем тесты на Espresso?
Рис. 2. Схема работы Espresso
1. На телефоне установлено приложение, которое мы собираемся тестировать
2. На телефон устанавливается тестовая арк, содержащая тесты которые мы написали
3. От компьютера по adb приходит команда Запустить тесты
4. Приложение и тестовая арк общаются друг с другом
В этой схеме Adb был потерян, компьютер после запуска тестов здесь не нужен.
Как можно возместить потерю?
Возникла идея, посылать из теста сигнал "Выполни Adb команду"
После исследования документации, было обнаружено, что на всех эмуляторах поднимается виртуальный роутер с рядом адресов, и один из них 10.0.2.2. Если перейти на эмуляторе на 10.0.2.2, он обратится к localhost вашего компьютера
Network Address Description
10.0.2.1 Router/gateway address
10.0.2.2 Special alias to your host ioopback interface (i.e., 127.0.0.1 on your development machine)
10.0.2.3 First DNS server
10.0.2.4/10.0.2.5/10.0.2.6 Optional second, third and fourth DNS server (if any)
10.0.2.15 The emulated device network/ethernet interface
127.0.0.1 The emulated device Ioopback interface
Рис. 3. Сетевые адреса Android Emulator Как только это было обнаружено, был реализован сервер на Flask
уду
Рис. 4. Сервер на Flask
1. Приложение с телефона обращается по адресу http://10.0.2.2 с указанием команды в cmd параметре
2. Сервер обрабатывает запрос
3. С компьютера отправляется указанная в cmd команда
Написали первые тесты, команды попадали на компьютер, он их исполнял. До тех пор, пока в тест-кейсе не столкнулись со строчкой "Отключить интернет". Как только отключается интернет, пропадает работа с виртуальным роутером. Почему так происходит?
Вспомним, что Android внутри Linux, и когда у нас включены wi-fi и мобильная сеть, мы можем зайти в shell и спросить какие интерфейсы подняты. Соответственно помимо localhost у нас есть wifi [wlanO] и мобильные данные [radioO].
$ adb shell
generic_x86:/ $ ifccrfig
radioO Link encap:UNSPEC
inet addr:192.168.200.2 Beast:192.168.200.255 Hask:255. inet6 addr: fec0::cc71:78ff:fee9:49df/64 Scope: Site inet6 addr: fe80:: cc71:78ff :fee9:ij9df /64 Scope: Link inet6 addr: fecC::odd:ea97:5b58:e6f2/64 Scape: Site UP BROADCAST RUNNING MULTICAST MTU:150O Metric:l RX packets:80 errors:0 dropped:G overruns:Q frame:© TX packets:91 errors:0 dropped:0 overruns:© carrier:0 collisions:© txqueuelen:1000 RX bytes:35762 TX bvtes:11113
wlanG Link encap:UNSPEC
UP BROADCAST MULTICAST MTU:15O0 Metric:l RX packets:© errors:B dropped:© overruns:© frame:0 TX packets:© errors:0 dropped:© overruns:© carriers collisions:© txqueuelen:1000 RX bytes:0 TX bytes:©
lo Link encap:UNSPEC
inet addr:l27.0,0,1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope: Host UP LOOPBACK RUNNING MTU:65536 Metrie:l RX packets:© errors:© dropped:0 overruns:© frame:0 TX packets:© errors:© dropped:0 overruns:© carrier:0 collisions:0 txqueuelen:© RX bytes:0 TX bytes:0
generic_x86:/ $
г -^ II
О
23:42 - С6,30 OKT. в ✓ ф -
V& шт
ч» аЛ Q
^^ AndroidWiti Android 100% v
в © Q
Не decrowns (в .шш (Чжич прпма
<
о
■ □
< • ■
;
Рис. 5. Интерфейсы Android Emulator при выключенном AirPlane Mode
Но как только мы нажимаем кнопку AirPlane Mode, все становится намного печальнее. У нас пропадают необходимые интерфейсы, и остаются лишь те, которые не позволяет работать с виртуальным роутером, установленным на эмуляторах
$ adb shell
generic_x86:/ $ ifconfig lo Link encap:UNSPEC
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope: Host
UP LOOPBACK RUNNING MTU:65536 Metric:l
RX packets:522 errors:0 dropped:© overruns:© frame:0
TX packets:522 errors:© dropped:© overruns:© carrier:©
collisions:© txqueuelen:©
RX bytes:38036 TX bytes:38036
generic_x86:/ $ []
*- -1 X -
О
23:43 • Сб, 30 окт. е / О ~ ■О
4v 4
Ч» V Q <5
WW Android 100% го
с Не беспокоить © Автоповорот (±) \ о
<
О о о
А Play Маркет □
в © ...
* • ■
Рис. 6. Интерфейсы Android Emulator при включенном AirPlane Mode
Так была потеряна фича.
И вновь обратившись к документации, мы нашли такую вещь как port forwarding.
Работает она так:
1. На телефоне поднимаем сервер, как в этом примере на :7100
2. На компьютере выполняется команда adb forward tcp:6100 tcp:7100
3. Обращаемся на компьютере по адресу localhost на порт 6100 и попадаем на сервер, который установлен на мобильном устройстве
Важно отметить, что это работает при полном отсутствии интернета, так как порты пробрасываются поверх подключения, по которому подключен эмулятор. Например, если телефон подключен по USB, нам не важно какие интерфейсы будут подняты у телефона, это будет происходить по USB.
Также в противовес команде forward есть команда adb reverse, которая позволяет в телефоне из приложения сделать http запрос на localhost телефона, при этом попадая на самом деле на компьютер.
Однако у этой команды есть ограничение, она работает только с Android 5.0, что нас немного не устраивало, так как была необходимость тестировать телефоны с версией ОС 4.2.
Финальная схема
Теперь, мы стали поднимать на компьютере клиент, а внутри тестовой апк у нас появилась такая сущность как сервер.
Вместо тысячи слов лучше сразу взглянуть на схему:
Рис. 7. Итоговая схема
Внимание! Схема непростая, поэтому мы постарались все досконально описать. Наберитесь терпения дочитать до конца, пожалуйста.
1. На телефоне выбираем порт. Так как со стороны девайса будет всегда одно соединение, то мы можем всегда ставить один и тот же порт. Пускай это будет :8500.
2. Со стороны десктопа соединений может быть множество, так как одновременно несколько девайсов могут быть к нему подключены. На каждое соединение создается уникальный незанятый порт из диапазона 6000.. 49000. В этом примере, для подключения двух девайсов, выберем порты :6100 и :48999.
3. На десктопе мы пробрасываем порты к каждому девайсу с помощью команд
adb -s devicel forward tcp:6100 tcp:8500
adb -s device2 forward tcp:48999 tcp:8500
4. На десктопе мы создаем сокетные клиенты по адресам localhost:6100 и localhost:48999. На всякий случай напомним, что сокетное соединение между клиентом и сервером происходит при условии наличия клиента и
сервера на одном адресе. То есть созданные клиенты будут ожидать появления сокетных серверов по обозначенным выше адресам (localhost:6100 и localhost:48999).
5. А теперь главный фокус. Мы пробросили порты :6100 и :48999 десктопа на :8500 девайсов. А это значит, что созданные сокетные клиенты на самом деле ждут сервера по адресу localhost:8500 девайсов!, а не по адресам localhost:6100 и localhost:48999 десктопа.
6. На каждом девайсе мы поднимаем сокетный сервер по адресу localhost:8500.
7. Происходит установка сокетного соединения на девайсе по адресу localhost:8500, где есть и клиент, и сервер. При этом помним, что физически сокетный клиент находится на десктопе (localhost:6100 и localhost:48999 на десктопе). Дальнейшее взаимодействие происходит по сокетному соединению. На девайсе нам может понадобится выполнение adb-команды, о чем мы сообщаем десктопу (передаем строку в канале), а десктоп уже физически исполняет данную команду с указанием девайса и отправляет по каналу ответ (строка со статусом).
Благодаря уведомлениям о выполнении команд в схему добавляется синхронность, так, например, при передаче больших файлов можно получить фидбек, когда команда будет выполнена.
Резюмируя, теперь можно использовать adb команды с условием того, что девайс подключен к компьютеру любым способом (с помощью WiFi, Bluetooth или USB).
Список литературы
1. Егор Курников. На чем писать Android UI-тесты. [Электронный ресурс], 2021. Режим доступа:https://habr.com/ru^ompany/avito/Ыog/516650/ (дата обращения: 19.11.2021).
2. Список команд adb emu. [Электронный ресурс], 2021. Режим доступа:https://developer.android.com/studio/run/emulator-console/ (дата обращения: 19.11.2021).
3. Документация Espresso. [Электронный ресурс], 2021. Режим доступа:https://developer.android.com/training/testing/espresso/ (дата обращения: 19.11.2021).
4. Документация UI Automator. [Электронный ресурс], 2021. Режим доступа:https://developer.android.com/training/testing/ui-automator/ (дата обращения: 19.11.2021).