Всем привет!

Продолжаем нашу серию статей о Kaspresso!

Это первая статья из раздела advanced, в котором мы будем рассказывать о тонкостях и деталях реализации фичей Kaspresso.

Когда сравнивают фреймворки автоматизации (На чем писать Android UI-тесты), в плюс Appium часто записывают исполнение adb-команд. А вот у Espresso и UI Automator этого функционала нет.

В Kaspresso мы попробовали это исправить и добавили AdbServer, о котором и поговорим.

Итак, поехали. В основе Kaspresso лежат Espresso и UI Automator, значит, при переходе с Appium мы теряем фичу — взаимодействие с устройством по adb

А зачем может пригодиться adb при прохождении тестов? Adb умеет выполнять не только такие стандартные команды, как установка приложения, скачивание каких-либо файлов с устройства, установка системных настроек, но и многие другие. Например, в Google сделали команду — adb emu, которая работает только с эмуляторами. Эта команда поднимает на эмуляторе Telnet-сервис, который позволяет управлять внутренним состоянием устройства и делать вот такие нестандартные вещи:

  • установка геопозиции;

  • симуляция прикосновения к сканеру отпечатка пальца;

  • нестабильное интернет-соединение;

  • звонок на телефон;

  • отправка смс;

  • многие другие.

Это все — полная эмуляция системных вызовов. В приложении не надо пытаться имитировать отсутствие интернета, потому что благодаря adb система показывает, как это произойдет на реальном устройстве.

Итак, мы убедились, что это нужная вещь. Продолжим.

Почему в Appium можно использовать adb, а на Espresso нельзя?

Схема тестирования на Appium выглядит так:

  1. Тестируемое приложение устанавливается на устройство.

  2. Тесты, которые мы написали, находятся на компьютере.

  3. Поднимается Selenium.

  4. Selenium поднимает Appium Server.

  5. Appium Server устанавливает на устройство Bootstrap.

  6. Bootstrap общается с Appium Server по adb.

То есть во время прогона тестов компьютер и мобильное устройство постоянно общаются по adb. Это позволяет  использовать adb и в самих тестах.

Что происходит, когда мы пишем тесты на Espresso?

  1. Тестируемое приложение устанавливается на устройстве.

  2. Test apk с тестами устанавливается на устройстве. 

  3. От компьютера по adb приходит команда Запустить тесты.

  4. Приложение и Test apk общаются друг с другом.

В этой схеме после запуска тестов компьютер никак не участвует, все происходит исключительно внутри устройства. Это значит, что adb использовать невозможно.

Как мы можем возместить потерю?

Возникла идея — посылать из теста сигнал «Выполни adb-команду».

Мы изучили документацию и обнаружили, что на всех эмуляторах поднимается виртуальный роутер с рядом адресов, один из которых 10.0.2.2

Если перейти на эмуляторе по 10.0.2.2, он обратится к localhost вашего компьютера.

Как только мы это выяснили, то  попробовали написать сервер на Flask.

Как мы видели схему взаимодействия:

  1. На устройстве приложение обращается по адресу http://10.0.2.2, где в cmd-параметре указана adb-команда.

  2. На компьютере сервер, запущенный на localhost, обрабатывает запрос.

  3. На компьютере выполняется adb-команда, указанная в cmd.

Мы написали первые тесты, adb-команды попадали на компьютер, он их исполнял. Мы были рады до тех пор, пока…

В тест-кейсе мы не столкнулись со строчкой «Отключить интернет».

Почему так происходит?

Вспомним, что внутри Android — Linux. Значит, когда у нас включены Wi-Fi и мобильная сеть, мы можем зайти в shell и спросить, какие интерфейсы подняты. 

То есть, кроме localhost, у нас есть Wi-Fi [wlan0] и мобильные данные [radio0].

Но, как только мы нажимаем кнопку AirPlane Mode, все становится намноооого печальнее. 

У нас пропадают необходимые интерфейсы, а оставшиеся не позволяют работать с виртуальным роутером, установленным на эмуляторах. Мы поняли, что потеряли фичу. 

Мы снова обратились к документации и  нашли любопытную вещь — port forwarding.

Работает она так:

  1. На устройстве поднимается сервер, как в этом примере, на :7100.

  2. На компьютере выполняется команда adb forward tcp:6100 tcp:7100.

  3. Обращаемся на компьютере по адресу localhost на порт :6100 и попадаем на сервер, который установлен на мобильном устройстве.

Важно отметить, что port forwarding работает при полном отсутствии интернета: порты пробрасываются поверх подключения, по которому подключен эмулятор. Например, если устройство подключено по USB, то порты пробросятся по USB, если подключено по Wi-Fi, то пробросятся по Wi-Fi. И при этом неважно, какие интерфейсы на устройстве подняты.

Также в противовес команде forward есть команда adb reverse. Она позволяет на устройстве из приложения сделать http-запрос на localhost устройства, но на самом деле попадать на компьютер. То есть делает все ровно наоборот.

Однако у этой команды есть ограничение: она работает только с Android 5.0. Нас это немного не устраивало, так как нужно было тестировать и на устройствах ниже Android 5.0.

Финальная схема, используемая в Kaspresso

Коротко о главном: мы стали поднимать на компьютере adbserver-desktop.jar. Это клиент, с помощью которого можно управлять подключенными устройствами. На устройствах гоняются тесты на Kaspresso. Именно связка adbserver-desktop.jar со стороны компьютера и Kaspresso со стороны устройства обеспечивает функционирование схемы ниже:

 1. На устройстве выбираем порт. Со стороны устройства будет всегда одно соединение, поэтому мы можем всегда ставить один и тот же порт. Пусть это будет :8500.

2. У компьютера может быть несколько соединений, так как к нему могут быть подключены несколько устройств одновременно. На каждое соединение создается уникальный незанятый порт из диапазона 6000..49000. На примере мы подключаем два устройства, для этого выбираем порты :6100 и :48999.

3. На компьютере пробрасываем порты к каждому устройству с помощью команд:

adb -s device1 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-команду. Об этом мы сообщаем компьютеру — передаем строку в канале. Компьютер физически исполняет данную команду с указанием устройства и отправляет по каналу ответ — строка со статусом.

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

Резюме: теперь в Kaspresso можно выполнять adb-команды, если устройство подключено к компьютеру любым способом (с помощью Wi-Fi, Bluetooth или по USB). 

Пока всё. Следите за дальнейшими анонсами Kaspresso tutorials! 

А пока добавляйте в закладки, что уже есть: 

Особая благодарность людям, которые помогли сделать статью лучше:

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