Привет, Хабр! Меня зовут Александр Карпенко, я QA Engineer в inDrive. Я подготовил эту статью для начинающих QA-специалистов. Ниже расскажу, как использовать Android Debug Bridge (ADB) в тестировании мобильных приложений и нужен ли вообще этот инструмент. 

Я думаю, базовые знания в тестировании у вас уже есть — поэтому не буду описывать процесс подготовки и настройки. Возможности ADB постоянно расширяются, но я поделюсь приемами, которые пригодятся ежедневно. Мой рассказ — о тестировании мобильных приложений, поэтому речь пойдет о macOS из-за возможности эффективно работать со всеми популярными мобильными платформами. На других ОС примеры могут незначительно отличаться, да простят меня адепты Windows.

Для начала коснемся самых базовых команд, без которых дальнейшее повествование будет нелогичным.

Вывод списка подключенных устройств и соединение с устройством

Обычно мы работаем с одним девайсом, но иногда подключаем несколько устройств — например, по TCP/IP.  Тогда указываем вручную, на каком из девайсов нужно выполнить команду. Выводим список всех подключенных устройств, чтобы получить идентификатор: 

adb devices — выводит список подключенных устройств. С ключом -l будет расширенный список свойств. Полезно, если подключено несколько устройств и сразу непонятно, какое нам нужно.

Чтобы указать ADB, с каким устройством нужно работать, следует прописать серийный номер устройства после ключа -s:

adb -s <serial_number> <command>, где <serial_number> — серийный номер устройства из списка и <command> — команда, которую надо выполнить на устройстве.

Например, установка приложения на конкретное устройство из списка:

adb -s 32312b96 install user/download/app.apk.

Еще один частый сценарий — одновременная работа с реальным девайсом и эмулятором, например, в роли исполнителя и заказчика. В таком случае легко различать девайсы не по серийному номеру, а с помощью ключей -d -e после команды adb.

Например,

adb -d install user/download/app.apk — команда будет выполнена на реальном устройстве с ключем -e на эмуляторе.

Также мы можем подключиться к устройству по TCP/IP, когда оно использует ту же Wi-Fi-сеть. Для этого подключаем устройство к ПК кабелем и меняем режим работы на девайсе с USB на TCP/IP командой adb tcpip 5555.

Вычисляем IP-адрес устройства любым доступным способом. Например, через настройки телефона в общей информации или с помощью команды

adb shell ifconfig wlan0.

Если к этому моменту вы уже отключили девайс от ПК, не забудьте дополнительно указать S/N устройства. Подключаемся к нему:

adb connect ip_address:5555.

Отключить устройство можно командой

adb disconnect ip_address:5555.

adb disconnect — отключить все наши TCP/IP устройства.

Для возврата в режим работы по USB используем команду

adb usb

(нижний регистр важен). 

Установка и удаление приложения, поиск пакета на устройстве

Установка приложения осуществляется командой

adb install <apk_path>, где <apk_path> — абсолютный путь до нашего APK-файла приложения. 

Приведу несколько полезных ключей после команды install, которые часто используются:

-d — переустановка с понижением версии. В противном случае будет ошибка Failure [INSTALL_FAILED_VERSION_DOWNGRADE].

-r — переустановить приложение с сохранением data.

-g — выдать при установке все пермишены, прописанные в манифесте приложения. Например, приложение, установленное с таким ключом, не будет запрашивать у вас разрешение на гео или доступ к хранилищу для загрузки фото. 

Удаление приложения происходит уже по имени пакета. Для этого нужно знать, как приложение регистрируется в системе. Используем оболочку Shell и менеджер пакетов Package Manager (pm).

Следующей командой выведем список всех установленных приложений:

adb shell pm list packages.

Можно отфильтровать список по имени приложения. Это понадобится, если список довольно большой, но мы знаем, какое слово присутствует в названии пакета:

adb shell pm list packages | grep com.myApp.

Также можно сделать вывод в отдельный файл и там найти нужный пакет: 

adb shell pm list packages > /Users/username/packages.txt.

Теперь, когда мы знаем, как вычислить имя пакета приложения. Вернемся к тому, как удалить его с устройства. Сделать это можно командой

adb uninstall com.myApp.

adb uninstall -k com.myApp — удаление приложения с сохранением data и кэша.

Отдельно приведу команду, которая часто может пригодиться:

adb shell pm clear com.myApp — почистить кеш и data приложения.

Загрузка APK файла с девайса

Думаю, это весьма редкий случай. Но, возможно, кому-то пригодится, как однажды пригодилось мне. Все установленные приложения хранят свой APK в папке /data/app. Поэтому, зная имя пакета, можно найти место, куда установлено приложение, и скачать оттуда его APK. Для этого выполним команду

adb shell pm path com.myApp — получим директорию установки приложения.

Это может выглядеть не совсем презентабельно:

package:/data/app/~~YcTsnr19yQR6ENa0q2EMag==/com.myApp—IHasf91SDB0erQLagc8j0Q==/base.apk

Но именно в таком виде нам и нужен этот путь. Немного забежим вперед и посмотрим, как можно скопировать на ПК с телефона нужный нам файл. Сделать это можно командой adb pull <crazyPath> /Users/username/, где <crazyPath> — результат вывода нашей предыдущей команды. А /Users/username/ — путь на ПК, куда нужно скопировать наш файл.

Текстовые поля 

Немного коснемся проверки текстовых полей. Например, нужно проверить ограничение на ввод максимального числа символов в поле. Если вы пользуетесь одним устройством, можно хранить разные наборы передаваемых данных на самом телефоне или в облаке. Но когда приходится проверять на разных девайсах, можно хранить данные для тестирования на ПК и передавать их на устройства следующими командами:

adb shell input text <text>.

Например:

adb shell input text test%stest — будет введена строка «test test». Пробелы заменяем спецсимволами %s, иначе на устройство будет передана только часть до пробела. Если в передаваемом тексте мы используем спецсимволы, вроде !@#, нужно выделять их обратным слэшем. 

Например, команда

adb shell input text test!@#$%stest выведет на экран “test!@#$ test”.

Важно: ADB не работает с кириллицей, получим NullPointerException.

Существует способ передачи буфера обмена:

adb shell input text $(pbpaste).

Нужно помнить, что некоторые символы могут не передаваться в том виде, в котором они отображаются на ПК. Проблему можно решить с помощью потокового редактора текста sed. Приведу пример расширенной команды, где мы заменяем все пробелы в буфере на нужные нам спецсимволы для корректной передачи текста на девайс:

adb shell input text $(pbpaste | sed -e 's/ /\%s/g')

pbpaste — текст, который содержится в буфере.

ключ «-e» — позволяет выполнить команды, которые необходимы для редактирования текста.

«s/что взять/на_что_поменять/опция» — шаблон.

/g — флаг для замены всех без исключения вхождений заданного шаблона.

Диплинки

Этот способ поможет проверить переходы по диплинкам на нужные экраны в следующих ситуациях:

  • Много экранов.

  • Не приходят пуши.

  • Пушей еще нет.

  • Проверка корректной работы приложения.

  • Проверка работы с некорректными пушами.

  • Переход по диплинку на экран, к которому нет доступа.

В Shell ADB мы можем выполнять команды с помощью Activity Manager (AM). 

Стартуем нашу активити и передаем диплинк, который мы хотим проверить. Обычно в диплинке присутствует символ &, который разделяет экраны. Поэтому при открытии через терминал нужно поставить перед ними обратный слэш (\):

adb shell am start -W -a android.intent.action.VIEW -d “myApp://open/client/trip\&last_trip=test” com.myApp

am — вызов Activity Manager.

W — ожидание загрузки перед выполнением команды.

a — определяем, какое действие будет выполнено. В данном случае action.View.

d — данные для запуска. В данном случае сам диплинк и далее приложение, через которое его следует открыть.

Возможно, при переносе команды в терминал придется переписать кавычки вручную или заменить на одинарные. Может ругаться на ошибку синтаксиса. 

Создание скриншотов и запись видео с экрана устройства

Сделаем скриншот этой командой:

adb shell screencap -p <device_path/screenshot_name.png>

Например:

adb shell screencap -p /sdcard/screencap.png — сделает скриншот экрана и сохранит файл с именем screencap.png на девайсе в папку /sdcard/screencap.png

Сохранить скрин на ПК можно так: 

adb pull /sdcard/screencap.png — по умолчанию файл копируется в директорию текущего пользователя /Users/username/screencap.png.

Или можно сразу запускать всю команду целиком:

adb shell screencap -p /sdcard/screencap.png && adb pull /sdcard/screencap.png.

На последних версиях ADB скриншот можно получить командой

adb exec-out screencap -p > screen.png — и файл со скриншотом также появится в директории текущего пользователя на ПК.

Установленный по умолчанию путь можно изменить вручную, добавив его в конце команды:

adb exec-out screencap -p > downloads/test/screen.png — и скриншот появится в папке /Users/username/downloads/test/screen.png. 

Также при желании можно немного автоматизировать этот процесс, добавив алиас в bash_profile. В macOS можно создать через Automator-службу и задать хоткей.

Для записи видео существует команда:

adb shell screenrecord <device_path>

Например:

adb shell screenrecord /sdcard/screenrecord.mp4 — команда начнет запись экрана устройства с использованием настроек по умолчанию в течении трех минут и сохранит результат записи в файле /sdcard/screenrecord.mp4 на устройстве. 

Можно вручную прописать время записи ключом -time-limit time (в секундах, правда запись все равно возможна не более 180 секунд).

Остановить запись раньше времени можно комбинацией клавиш CTRL+C.

Скопировать файл можно также через команду pull по аналогии со скриншотом.

Также можно посмотреть дополнительные возможности этой утилиты с помощью ключа --help. К слову, она умеет изменять разрешение записи, битрейт, добавить дополнительные данные для багрепорта.

Полезно пользоваться ключом -bugreport, который добавляет первым кадром в видео информацию о системе, на которой происходила запись.

Про то, как стянуть с девайса что-либо мы поговорили, теперь поговорим немного о том, как туда что-нибудь закинуть.

Открыли на ПК, изменили формат, содержание, закинули на телефон, проверили, что приложение корректно реагирует на неизвестные форматы, на превышение размера. Загрузить файл на телефон с ПК можно командой:

adb push /Users/username/file <device_path>

Выполним команду:

adb push /Users/username/screen.png sdcard — в результате наш файл screen.png скопируется на телефон в раздел sdcard.

Проверка восстановления состояния приложения после его убийства системой

Еще один пример из опыта связан с проверкой восстановления стейта приложения после его убийства системой. Сворачиваем приложение, убиваем процесс — это действие имитирует остановку процесса системой в случае нехватки памяти:

adb shell am kill com.myApp

Запускаем снова, смотрим, что ничего не сломалось.

Мы столкнулись с таким сценарием: пользователь сворачивает приложение, находясь на определенном экране. Через какое-то время система тормозит процесс и кэширует его стейт. Когда пользователь пытается развернуть приложение, получает краш. Это происходит при обращении к данным из кэша, так как фрагменты восстанавливают свой стек и состояние, но кэш уже пустой.

К сожалению, этот баг мы не поймали на тестировании, потому что не сталкивались с таким раньше, и он ушел в прод. Но теперь вы знаете, что такое возможно, и не допустите наших ошибок.

Логи

Работа с логами полезна, когда мы ищем причину краша приложения. Если нужно сохранить текущий буфер логов, сделать это можно командой:

adb logcat — выводит логи в реальном времени.

adb logcat -d — выводит лог на момент запуска команды, не дописывая реальные события на устройстве. Также можно вывести лог в отдельный файл командой: adb logcat -d > file.log (файл создается в директории текущего пользователя).

А команда adb logcat >> file.log будет писать лог сразу в файл, дописывая все реальные события на устройстве.

Существует несколько уровней по мере возрастания: V — Verbose, D — Debug, I — Info, W — Warn, E — Error, F — Fatal, S — Silent. Например:

adb logcat '*:E' — будет выводить логи с ошибками и уровнем выше.

Теперь немного о форматировании вывода и фильтрах, с помощью ключа -v можно изменить формат вывода в консоль:

adb logcat -v time — выводит логи последовательно по времени записи.

adb logcat -v color — отображает каждый уровень логов отдельным цветом, очень помогает при чтении.

adb logcat -v brief — отображает приоритет, тег и PID процесса.

Каждое сообщение журнала имеет тег и связанный с ним приоритет. С их помощью можно уменьшить количество вывода в консоль: 

adb logcat SwrveSDK:I '*:S' — будет отображать наши отправляемые события аналитики в сервис Swrve. Параметр *:S говорит о том, что вывод журнала ограничен выражением фильтра.

Ну и всегда можно использовать утилиту grep для фильтрования вывода:

adb logcat '*:E' -v color | grep com.myApp

По традиции, за более дополнительной информацией всегда можно обратиться к помощнику adb logcat --help.

Теперь о том, как это использовать. Например, если баг воспроизвелся, а устройство не было подключено, можно сразу подключить его и перенаправить лог в файл. 

Для последующего дебага перед сбором логов можно чистить буфер, чтобы исключить лишние данные. Сделать это можно командой: adb logcat -c, дальше воспроизводим баг и делаемadb logcat -d.

Для любителей покопаться в куче логов есть еще один инструмент — ADB bugreport. Позволяет создавать ZIP-архивы с полной отладочной информацией в простом текстовом формате (.txt).

adb bugreport /Users/username — создает zip архив в указанной директории.

Копирует всю информацию об устройстве, такую ​​как данные dumpstate, dumpsys и logcat в указанную папку. По умолчанию отчеты об ошибках сохраняются в /bugreports и могут быть просмотрены с помощью:

adb shell ls /bugreports/

Самая важная для нас информация хранится в bugreport—BUILD_ID—DATE.txt

Есть еще один интересный инструмент для работы с крашами — ANR, когда приложение не отвечает Application Not Responding. Запускаем его командой:

adb shell am monitor, и далее воспроизводим наш краш. В консоль будет выведена информация о краше без лишней воды и три варианта продолжения работы нашего мониторинга: (c)ontinue: show crash dialog, (k)ill: immediately kill app, (q)uit: finish monitoring.

Эмуляторы

Вывод списка настроенных эмуляторов:

emulator -list-avds

Запуск нужного нам эмулятора:

emulator @avdname.

При работе с эмуляторами бывает, что необходимо перезапустить службы. При этом сервер нужно запустить после старта эмулятора, но часто достаточно и одной команды: adb kill-server. Если не помогло, выполняем весь сценарий:

emulator -list-avds — выведем список настроенных эмуляторов.

adb kill-server — останавливаем сервер.

emulator -avd avdname (или emulator @avdname) — где avdname — имя эмулятора.

adb start-server — заново запускаем сервер.

adb devices — выводим список подключенных устройств, наш потерянный эмулятор должен появиться.

Для самых ленивых, можно создать эмулятор из командной строки. Например, следующая команда создает эмулятор с именем “test”, используя системный x86-образ с API 25:

avdmanager create avd -n test -k "system—images;android-25;google_apis;x86"

Если нужного образа нет, можно предварительно установить его командой:

sdkmanager --install "system—images;android—25;google_apis;x86"

sdkmanager --list | grep system—images — выведет список доступных для скачивания образов.

С эмуляторами также во время работы иногда возникают фантомные проблемы, и одна из частых команд, которая помогает это загрузка эмулятора на холодную без подтягивания автоматического снапшота, при этом на выходе снапшот будет сделан:

emulator @avdname -no-snapshot-load

Еще несколько полезных ключей при старте эмулятора: 

-no-snapshot-save — не будет автоматического сохранения снапшота.

-no-snapshot — не будет ни загрузки, ни сохранения снапшота.

Если эмулятору по-прежнему плохо, можно очистить его с помощью ключа, который возвращает эмулятор в первоначальное состояние: -wipe-data.

Создание снапшотов — весьма полезный инструмент для сохранения разных состояний девайса. Вручную это можно делать через настройки эмулятора, либо с помощью команды:

adb emu avd snapshot save test — сохраняем состояние эмулятора, где test — имя снапшота, которое будет храниться на девайсе.

emulator @avdname -snapshot-list — запускаем наш эмулятор с именем @avdname с выводом в консоль списка снапшотов

Далее можно загрузить сохраненный ранее снапшот командой:

adb emu avd snapshot load test — где test — имя сохраненного ранее снапшота.

adb emu avd snapshot delete test — удаляет снапшот с именем test.

Можно сразу запускать эмулятор с нужным нам снапшотом: emulator @avdname -snapshot test.

Также с помощью команды pull можно стащить снапшот с девайса:

adb emu avd snapshot pull test /Users/username/.

С нашим эмулятором можно работать через консоль telnet. Но для этого сначала надо ее установить. Самый простой способ — через менеджер пакетов brew, если он у вас есть. А если нет, самое время узнать, что это и как им пользоваться. Итак, устанавливаем telnet командой brew install telnet.

Далее запускаем наш эмулятор. В другой вкладке терминала подключаемся к нему командой telnet localhost port.

telnet localhost 5554 — подключиться к нашему эмулятору, использующего порт 5554.

После отработки команды мы можем делать всякие полезные штуки с нашим эмулятором, в том числе, работать с geo. Например, команда geo fix 40.748840 —73.984279 установит нужное нам местоположение по указанным координатам.

Например, работа со снапшотами немного упрощается, команды из предыдущего раздела сокращаются до avd snapshot <command>.

Изменение разрешения на девайсе

Для проверки корректности отображения элементов на экране устройства есть полезные команды менеджера окон (wm), позволяющие изменять разрешение и плотность пикселей. Так можно перебрать все необходимые варианты размеров экрана и посмотреть, как наше приложение будет адаптироваться под них: 

adb shell wm size 1080x1920 — установить кастомное разрешение экрана, где ширина будет равна 1080, а высота — 1920.

adb shell wm size reset — сбросить все наши изменения.

adb shell wm density X — менять плотность пикселей, где минимальное значение — 72. Чем больше значение, тем крупнее элементы на экране.

adb shell wm density reset — сбросить все наши изменения.

Если мы запустим наши команды без аргументов, нам вернется текущее разрешение экрана и плотность пикселей подключенного устройства или эмулятора.

Monkey

Отдельно можно упомянуть инструмент Monkey, который генерирует случайные пользовательские события на эмуляторе или устройстве: клики, касания и жесты, а также ряд событий системного уровня, что напоминает движения глупой мартышки. Можно использовать Monkey для стресс-тестирования.

adb shell monkey — вывод всех параметров обезьянки.

Пример полного сценария: adb shell monkey--throttle 100--pct—syskeys 0 -p com.myApp -v 10.

Ключ  -throttle — задержка между действиями в миллисекундах. Так как Monkey выполняет свои действия довольно быстро, этот ключ обычно используется, когда мы хотим визуально контролировать происходящее на экране.

Ключ -pct-syskeys — определяет процент системных кнопок, которые будут нажаты в процессе сценария. В данном примере установлено значение 0, что говорит о том, что системные кнопки не будут нажиматься совсем. 

Ключ -p — имя пакета, который мы передаем.

Ключ -v —  количество действий, которые нужно выполнить.

Разрешения приложения

Работа с пермишенами заключается в их отзыве у приложения, потому что предоставление разрешение обычно идет через запрос самого приложения. Это делается быстро и просто, тогда как отзыв разрешения идет через системные настройки.

adb shell dumpsys package com.MyApp | grep permission— выводит список из доступных разрешений приложения. Например, install permissions — обязательные разрешение, которые выдаются при установки приложения. runtime permissions — разрешения, которые запрашиваются в конкретный момент: например, при обращении к файловому хранилищу. Замечу, что если в списке requested permission нет какого-либо разрешения, выдать к нему доступ не получится.

Итак, чтоб отозвать разрешение у нашего приложения, нужно выполнить команду adb shell pm revoke packageName permissionName.

adb shell pm revoke com.MyApp android.permission.CAMERA — отзовет у приложения com.myApp доступ к камере. После возврата в приложение и попытке использовать через него камеру мы снова увидим запрос на предоставление разрешения. 

Командой grant мы выдаем разрешение приложению. Например,

adb shell pm grant com.myApp android.permission.CAMERA — для нашего приложения будет выдан доступ на использование камеры телефона.

Батарея

Немного коснемся работы с батареей, а также затронем режим ожидания.

adb shell dumpsys battery — вывод информации о батареи.

adb shell dumpsys battery set level X — установка уровня заряда батареи, где X — процент заряда.

adb shell dumpsys battery unplug — имитация отключения зарядки.

adb shell dumpsys battery reset — сброс всех наших изменений.

Теперь поговорим про режимах ожидания. Начиная с Android 6.0, появилась функция Doze Mode. Она направлена на экономию заряда и продление срока службы батареи за счет ограничения активности приложений после того, как пользователь не взаимодействовал с устройством и оно не находилось на зарядке.

При этом система периодически выходит из Doze Mode для выполнения отложенных фоновых задач. Еще один схожий инструмент Android — App Standby. В отличие от Doze Mode, это состояние конкретного приложения, которое находится в фоне определенный период времени и после этого входит в режим Standby. Наша задача — убедиться в том, что приложение нормально восстанавливает свою работу после выхода из этих двух режимов энергосбережения.

Для перехода устройства в режим Doze Mode нужно выполнить следующие команды:

adb shell dumpsys battery unplug — отключить зарядку.

adb shell dumpsys deviceidle step — команду, возможно, придется выполнить несколько раз, пока она не вернет Stepped to deep: IDLE.

После всех манипуляций с батареей лучше выполнить командуadb shell dumpsys battery reset, вернув ее в исходное состояние.

Также есть команда для принудительного ввода устройства в Doze Mode: adb shell dumpsys deviceidle force-idle, иногда перед этим нужно выполнить команду adb shell dumpsys deviceidle enable.

Вывести обратно из состояния Doze Mode можно командойadb shell dumpsys deviceidle unforce. Не забываем сбросить состояние батареи: adb shell dumpsys battery reset.

Теперь немного про App Standby. Для перевода приложения в данный режим нужно выполнить следующие команды:

adb shell dumpsys battery unplug — отключаем батарею, как и в предыдущем случае.

adb shell am set—inactive com.myApp true — вводим приложение в режим App Standby.

Далее выводим наше приложение из режима App Standby командой adb shell am set-inactive com.myApp false.

Проверить статус приложения можно командой adb shell am get-inactive com.myApp.

Еще немного полезных команд

adb reboot — перезагрузка устройства, актуально и для реального девайса.

adb shell dumpsys package com.myApp — вывести полную информацию о конкретном приложении.

adb shell dumpsys meminfo com.myApp — посмотреть использование памяти приложением на девайсе от занимаемого места до отображения баз данных, используемых этим приложением.

adb shell getprop — получить список доступных свойств устройства: производитель, модель устройства, хардварные спецификации и другое.

Получить список доступных для приложения Activity: adb shell dumpsys package com.myApp | grep -i Activity.

Получить имя запущенной активити: adb shell dumpsys window | grep Focused.

Запустить выбранную активити приложения: adb shell am start -n com.myApp/.ActivityClass —  можно запускать любые установленные приложения, в том числе и системные. Например: adb shell am start -n com.android.settings/.Settings — запустит наши настройки телефона.

Сделать вызов на указанный телефонный номер: adb shell am start -a android.intent.action.CALL tel:+790900000XX.

Открыть страницу в браузере: adb shell am start -a android.intent.action.VIEW 'https://indriver.com'

В конце хочу сказать, что все возможности Android Debug Bridge невозможно запихнуть в одну статью, как и досконально изучить их работу. Постоянно что-то меняется: что работало сегодня, внезапно может перестать работать завтра. А потребности в знаниях тех или иных инструментов будут возникать по мере поиска решения конкретных задач.

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

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


  1. GavriKos
    14.10.2022 11:40
    +4

    Статья отличная, но чет форматирование какое то... А так пушка-бомба