Всем привет!
Продолжаем нашу серию статей о 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 выглядит так:
Тестируемое приложение устанавливается на устройство.
Тесты, которые мы написали, находятся на компьютере.
Поднимается Selenium.
Selenium поднимает Appium Server.
Appium Server устанавливает на устройство Bootstrap.
Bootstrap общается с Appium Server по adb.
То есть во время прогона тестов компьютер и мобильное устройство постоянно общаются по adb. Это позволяет использовать adb и в самих тестах.
Что происходит, когда мы пишем тесты на Espresso?
Тестируемое приложение устанавливается на устройстве.
Test apk с тестами устанавливается на устройстве.
От компьютера по adb приходит команда Запустить тесты.
Приложение и Test apk общаются друг с другом.
В этой схеме после запуска тестов компьютер никак не участвует, все происходит исключительно внутри устройства. Это значит, что adb использовать невозможно.
Как мы можем возместить потерю?
Возникла идея — посылать из теста сигнал «Выполни adb-команду».
Мы изучили документацию и обнаружили, что на всех эмуляторах поднимается виртуальный роутер с рядом адресов, один из которых 10.0.2.2.
Если перейти на эмуляторе по 10.0.2.2, он обратится к localhost вашего компьютера.
Как только мы это выяснили, то попробовали написать сервер на Flask.
Как мы видели схему взаимодействия:
На устройстве приложение обращается по адресу http://10.0.2.2, где в cmd-параметре указана adb-команда.
На компьютере сервер, запущенный на localhost, обрабатывает запрос.
На компьютере выполняется adb-команда, указанная в cmd.
Мы написали первые тесты, adb-команды попадали на компьютер, он их исполнял. Мы были рады до тех пор, пока…
В тест-кейсе мы не столкнулись со строчкой «Отключить интернет».
Почему так происходит?
Вспомним, что внутри Android — Linux. Значит, когда у нас включены Wi-Fi и мобильная сеть, мы можем зайти в shell и спросить, какие интерфейсы подняты.
То есть, кроме localhost, у нас есть Wi-Fi [wlan0] и мобильные данные [radio0].
Но, как только мы нажимаем кнопку AirPlane Mode, все становится намноооого печальнее.
У нас пропадают необходимые интерфейсы, а оставшиеся не позволяют работать с виртуальным роутером, установленным на эмуляторах. Мы поняли, что потеряли фичу.
Мы снова обратились к документации и нашли любопытную вещь — port forwarding.
Работает она так:
На устройстве поднимается сервер, как в этом примере, на :7100.
На компьютере выполняется команда adb forward tcp:6100 tcp:7100.
Обращаемся на компьютере по адресу 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!
А пока добавляйте в закладки, что уже есть: