Всем привет!
Продолжаем нашу серию статей о 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 выглядит так:
![](https://habrastorage.org/getpro/habr/upload_files/e55/06a/966/e5506a9664d1d6c23b195d565f133b67.png)
Тестируемое приложение устанавливается на устройство.
Тесты, которые мы написали, находятся на компьютере.
Поднимается Selenium.
Selenium поднимает Appium Server.
Appium Server устанавливает на устройство Bootstrap.
Bootstrap общается с Appium Server по adb.
То есть во время прогона тестов компьютер и мобильное устройство постоянно общаются по adb. Это позволяет использовать adb и в самих тестах.
Что происходит, когда мы пишем тесты на Espresso?
![](https://habrastorage.org/getpro/habr/upload_files/007/343/883/007343883d43ca8d1bb7832da69ee70f.png)
Тестируемое приложение устанавливается на устройстве.
Test apk с тестами устанавливается на устройстве.
От компьютера по adb приходит команда Запустить тесты.
Приложение и Test apk общаются друг с другом.
В этой схеме после запуска тестов компьютер никак не участвует, все происходит исключительно внутри устройства. Это значит, что adb использовать невозможно.
Как мы можем возместить потерю?
Возникла идея — посылать из теста сигнал «Выполни adb-команду».
Мы изучили документацию и обнаружили, что на всех эмуляторах поднимается виртуальный роутер с рядом адресов, один из которых 10.0.2.2.
![](https://habrastorage.org/getpro/habr/upload_files/4e9/c1e/208/4e9c1e208749fb0fa5b2cadab4e256ee.png)
Если перейти на эмуляторе по 10.0.2.2, он обратится к localhost вашего компьютера.
Как только мы это выяснили, то попробовали написать сервер на Flask.
Как мы видели схему взаимодействия:
![](https://habrastorage.org/getpro/habr/upload_files/fac/7c2/18e/fac7c218e45cec8bb1e01aae3b9298c9.png)
На устройстве приложение обращается по адресу http://10.0.2.2, где в cmd-параметре указана adb-команда.
На компьютере сервер, запущенный на localhost, обрабатывает запрос.
На компьютере выполняется adb-команда, указанная в cmd.
Мы написали первые тесты, adb-команды попадали на компьютер, он их исполнял. Мы были рады до тех пор, пока…
![](https://habrastorage.org/getpro/habr/upload_files/31c/97d/cfe/31c97dcfe2072e5a36e151ef96b3307e.png)
В тест-кейсе мы не столкнулись со строчкой «Отключить интернет».
![](https://habrastorage.org/getpro/habr/upload_files/1d3/2be/015/1d32be015e5cf192e831f3a11b0f85aa.png)
Почему так происходит?
Вспомним, что внутри Android — Linux. Значит, когда у нас включены Wi-Fi и мобильная сеть, мы можем зайти в shell и спросить, какие интерфейсы подняты.
![](https://habrastorage.org/getpro/habr/upload_files/cf3/07e/dee/cf307edee6a684bb48e555799406bdf8.png)
То есть, кроме localhost, у нас есть Wi-Fi [wlan0] и мобильные данные [radio0].
Но, как только мы нажимаем кнопку AirPlane Mode, все становится намноооого печальнее.
![](https://habrastorage.org/getpro/habr/upload_files/378/dff/7f2/378dff7f264b88108fa50ac0124a3267.png)
У нас пропадают необходимые интерфейсы, а оставшиеся не позволяют работать с виртуальным роутером, установленным на эмуляторах. Мы поняли, что потеряли фичу.
Мы снова обратились к документации и нашли любопытную вещь — port forwarding.
Работает она так:
![](https://habrastorage.org/getpro/habr/upload_files/d64/0a9/5e5/d640a95e55265d42c930ad3de183ea34.png)
На устройстве поднимается сервер, как в этом примере, на :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 со стороны устройства обеспечивает функционирование схемы ниже:
![](https://habrastorage.org/getpro/habr/upload_files/90b/b30/728/90bb3072896f922f5d45a1cbaad6d2aa.png)
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!
А пока добавляйте в закладки, что уже есть: