Hello, Hackernoon! My name is Alexander Karpenko, and I work as a QA Engineer at inDrive. I have prepared this article for novice QA specialists. Below, I will tell you how to use Android Debug Bridge (ADB) in mobile application testing and why it’s needed in the first place.
I imagine you already have basic knowledge of testing fundamentals so I will skip the process of preparing and setting up projects.
ADB's capabilities are constantly expanding, but I will share some valuable techniques that will improve your daily workflow. As my story is about mobile app testing, I will focus on macOS, which lets you work effectively with all popular mobile platforms. For other operating systems, the examples might be slightly different, but hopefully, Windows users won’t hold this against me.
To begin with, let's touch on the most basic commands to make sure the later points are presented in a logical sequence.
Normally, we work with one device, but sometimes several devices are connected, e.g., via TCP/IP. In this case, we have to manually specify the device we want to run the command on.
adb devices
— displays the list of connected devices (using the -l
switch pulls up an extended list of properties). This is useful if multiple devices are connected and it’s not immediately clear which one we need.
To tell ADB which device to target, the serial number of the device must be specified after the -s
switch:
adb -s <serial_number> <command>
, where <serial_number>
is the serial number of the device from the list and <command>
is the command to execute on the device.
For example, installing an application on a specific device from the list:
adb -s 32312b96 install user/download/app.apk
Another frequent scenario is when the operation simultaneously involves a real device and an emulator, e.g., for the different roles of executor/originator. In this case, the devices are easily distinguishable not by their serial number but based on the —d —e
switches after the adbcommand. Example:
adb —d install user/download/app.apk
— the command will be executed on the real device, with the —е
switch on the emulator.
We can also connect to the device over TCP/IP when it uses the same Wi-Fi network. To do this, connect the device to the PC with a cable and change the operating mode on the device from USB to TCP/IP by using the command:
adb tcpip 5555
For example, via the phone settings in General Information or with the command:
adb shell ifconfig wlan0
If your device is already disconnected from the PC at this point, be sure to additionally specify the S/N of the device. Next, we connect to it:
adb connect ip_address:5555
The device can be disabled with the command:
adb disconnect ip_address:5555
adb disconnect
— to disable all of our TCP/IP devices.
To return to USB mode, use the command:
adb usb
(lowercase is important).
The application is installed using the command:
adb install <apk_path>
, where <apk_path>
is the absolute path to our APK application file.
Here are some useful switches after the install command that are often used:
—d
— reinstalls with a downgraded version. Otherwise, there will be a failure (error) [INSTALL_FAILED_VERSION_DOWNGRADE]
).
—r
— reinstalls the application with data saved.
—g
— grants all permissions specified in the application manifest during the installation process.
For example, an app installed with this switch will not ask you to allow access to geolocation or storage space for downloading photos.
The application is uninstalled based on the name of the package. To do this, you need to know how the application is registered in the system. Use the Shell and thePackage Manager (pm).
Run the following command to display the list of all installed applications:
adb shell pm list packages
The list can be filtered by application name. You will need this if the list is quite large, but we know which word is contained in the name of the package:
adb shell pm list packages | grep com.myApp
You can also output to a separate file and find the package needed there:
adb shell pm list packages > /Users/username/packages.txt
Now that we know how to figure out the name of the application package, let's go back to how to remove it from the device. This can be done using the command:
adb uninstall com.myApp
adb uninstall -k com.myApp
— deletes the application but saves data and cache files.
I will separately present a command that may often prove useful:
adb shell pm clear com.myApp
— cleans up the cache and data of the app.
I think this is a rare situation to come across. But this might come in handy for someone, just as it once did for me. All installed applications store their APK files in the /data/appfolder. Since you know the name of the package, you can find the place where the application is installed and download its APK from there. To do this, run the following command:
adb shell pm path com.myApp
— displays the installation directory of the application.
This may not look all that presentable:
package:/data/app/~~YcTsnr19yQR6ENa0q2EMag==/com.myApp—IHasf91SDB0erQLagc8j0Q==/base.apk
But this is exactly how we need this path to look. Let's skip a little bit ahead and see how we can copy the file we need to the PC from the phone. This can be done using the command:
adb pull <crazyPath> /Users/username/
, where <crazyPath>
is the output of our previous command, /Users/username/
is the path on the PC where you want to copy our file to.
Let's briefly talk about checking text fields. For instance, you have to check the limit for the maximum number of characters that can be entered into a text field. If you use a single device, different sets of transferred data can be stored on the phone or in the cloud. However, if the testing has to be done on different devices, the test data can be stored on the PC and transferred to the devices via the following commands:
adb shell input text <text>
Example:
adb shell input text test%stest
— the string "test test" will be entered. Replace the spaces with the special characters %s
, otherwise, only the part preceding the space will be sent to the device. If we use special characters like !@#
in the text being transmitted, they have to be marked by inserting a backslash ( \
) in front of them.
For example, the command:
adb shell input text test\!\@\#\$%stest
will display “test!@#$ test”
on-screen
(ADB doesn’t work with Cyrillic characters and generates a NullPointerException error).
The contents of a clipboard can be transferred this way:
adb shell input text $(pbpaste)
Keep in mind that some characters may not be transmitted as they appear on the PC. The problem can be solved by using a streaming text editor (sed). Here is an example of an extended command where we replace all the spaces in the buffer with the special characters we need to make sure the text is transferred to the device in the correct manner:
adb shell input text $(pbpaste | sed -e 's/ /\%s/g')
pbpaste
is the text that is contained in the buffer.
The ”—e”
switch— allows you to execute the commands needed to edit text.
“s/take this/change_it_to/option”
is the template (pattern) to use.
/g
is the flag to replace all matches for a particular template, without exception.
Let’s keep in mind that it’s best when the testing is done in an environment as close to real life as possible, but it’s helpful to know that this option is also available.
This strategy will help you check the deep links to the screens needed, when multiple screens are used, or if we have problems with the infrastructure and no push notifications come through, or if none of them have arrived yet but you need to check how the application behaves.
You can also check to see if the application works correctly and doesn’t crash if an invalid deep link appears in the push notification alert. Or when dealing with a situation where we follow a deep link to a screen that no longer exists, or whose status has changed, or if we shouldn't have access to that screen in the first place, because the push alert has been in the notifications for a long time. There may be a lot of situations like this.
In Shell ADB, we can use the Activity Manager (AM) to execute commands.
We start off our activity and send the deep link that we want to check. The deep link usually contains the character “&”, which separates the screens. Therefore, when opening through a terminal, a backslash (\) has to be inserted in front of it.
adb shell am start -W -a android.intent.action.VIEW -d “myApp://open/client/trip\&last_trip=test” com.myApp
am
— calls up the Activity Manager.
W
— waiting for download before executing a command.
a
— determines which action is to be taken. In this case, it’s action.View.
d
— data needed for the run. In this case, we’re talking about the deep link itself and then the application that should be used to open it.
You might have to re-insert the quotation marks manually or replace them with single quotes when transferring the command to the terminal. If there's a syntax error, you may get an appropriate message.
Use this command to take a screenshot:
adb shell screencap -p <device_path/screenshot_name.png>
Example:
adb shell screencap -p /sdcard/screencap.png
— takes a screenshot and saves a file named screencap.png
on the device to the /sdcard/screencap.png
folder.
You can save the screenshot on your computer as follows:
adb pull /sdcard/screencap.png
— by default, the file is copied to the directory of the current user, i.e., /Users/username/screencap.png
.
Or you can run the entire command at once:
adb shell screencap -p /sdcard/screencap.png && adb pull /sdcard/screencap.png
In the latest versions of ADB, the following command can be used to get a screenshot:
adb exec—out screencap —p > screen.png
— the screenshot file will also appear in the current user’s directory on the PC.
The default path can be changed manually by being added at the end of the command:
adb exec—out screencap -p > downloads/test/screen.png
— and the screenshot will appear in the /Users/username/downloads/test/screen.png
folder.
If interested, you can also automate this process a bit by adding an alias to bash_profile
. In macOS, you can use the Automator Service to create and set up hotkeys.
Use this command to record a video:
adb shell screenrecord device_path
.
Example:
adb shell screenrecord /sdcard/screenrecord.mp4
— use this command to start recording the device screen based on the default settings for three minutes, and save the result to the file /sdcard/screenrecord.mp4
on the device.
You can manually specify the recording time by using the —time—limit time
switch (in seconds, but the length of the recording is still limited to 180 seconds).
The recording can be stopped ahead of time by pressing CTRL + C
The file can also be copied by using the pull command, in a similar way to the screenshot procedure.
You can also check out the additional features of this utility by using the --help
switch. Incidentally, it is able to change the recording resolution and bitrate, as well as add additional data for the bug report.
It’s helpful to use the -bugreport
switch, which adds information about the system used for the recording as the first frame in the video.
Now that we have covered how to download some content from the device, let's focus a little on how to upload something to it.
We opened it on our PC, adjusted the format and content, made a download to our phone, and checked to make sure that the application responds correctly to unknown formats and oversize limits. To upload a file to your phone from your PC, you can use this command:
adb push /Users/username/file <device_path>
Let's run the command:
adb push /Users/username/screen.png sdcard
— this will copy our screen.png
file to the phone's SD card.
Another example from experience is concerned with checking to see if the app’s state has been restored after its deletion by the system. Collapse the application, kill the process - this action simulates the situation when the system stops the process because there is not enough memory available:
adb shell am kill com.myApp
Run it again and check to see if everything is working fine.
We came across this scenario: a user minimizes an application while on a certain screen. After a while, the system slows down the process and caches its state. When the user tries to expand the application, it crashes. This happens when data from the cache is accessed, because the fragments are restoring their stack and state, but the cache is already empty. Unfortunately, this bug was overlooked during the testing phase, because we had not encountered it before, and so it ended up in production. Now that you know it's possible, you can make sure that you don't repeat our mistakes.
It’s a good idea to check the logs when trying to find out what caused an application to crash. If you want to save the current log buffer content, this can be done by using the following command:
adb logcat
— displays logs in real time.
adb logcat —d
— displays log information at the moment the command is run, without adding real events on the device. It’s also possible to output the log to a separate file by using the command: adb logcat —d > file.log
(the file is created in the directory of the current user).
And the command adb logcat >> file.log
will write the log directly into the file, adding all the real events on the device.
There are several levels in ascending order of priority: V — Verbose, D — Debug, I — Info, W — Warn, E — Error, F — Fatal, S — Silent, for instance:
adb logcat '*:E'
— will output logs with errors and a level higher.
Now let’s dwell briefly on output formatting and filters. You can change the format of the output to the console by using the -v switch, for example:
adb logcat -v time
— outputs logs sequentially by recording time points.
adb logcat -v color
— displays each level of logs in a different color (which is helpful when reading).
adb logcat -v brief
— displays the process priority, tag, and PID.
Each log message has a tag and its related priority. You can use them to reduce the amount of output to the console:
adb logcat SwrveSDK:I '*:S'
— will display the analytics events we send via the swrve service. The *:S (-s)
parameter indicates that the log output is limited to the filter expression we explicitly specified.
And as always, you can use the grep utility to filter the output:
adb logcat '*:E' —v color | grep com.myApp
Traditionally, for further information, you can always turn to your assistant adb logcat --help
For example, if a bug is reproduced while the device is not connected, you can immediately connect it and redirect the log to a file.
For further debugging, before collecting logs, you can pre-clear the log buffer before reproducing the bug to eliminate superfluous data. This can be done via the command:
adb logcat —c
, then we reproduce the bug and run adb logcat —d
For those who like to dig through piles of logs, there is another tool to consider — ADB bugreport. This tool allows you to create zip archives with full debugging information in plain text format (.txt
).
adb bugreport /Users/username
— creates a zip archive in the specified directory.
Copies all information about the device, such asdumpstate, dumpsys, and logcat data to the specified folder. By default, error reports are stored in /bugreports and can be viewed via:
adb shell ls /bugreports/
The most important information for us is stored in bugreport-BUILD_ID-DATE.txt
Crash Monitoring and ANR (Application Not Responding) is another interesting tool for working with crashes. Run it by using this command:
adb shell am monitor
, and then we reproduce our crash. The console will display information about the crash without any redundant details and three options for continuing our monitoring efforts: (c)ontinue: show crash dialog, (k)ill: immediately kill app, (q)uit: finish monitoring
.
Displaying a list of configured emulators:
emulator -list-avds
Running the emulator we need:
emulator @avdname
When working with emulators, sometimes it’s necessary to restart services, and the server must be reset once the emulator is started, but fairly often even one command is enough: adb kill-server
. If this doesn’t help, then run the entire script:
emulator -list-avds
— displays a list of configured emulators.
adb kill-server
— stops the server.
emulator -avd avdname
(or emulator @avdname
)— where avdname
is the name of the emulator.
adb start—server
— restarts the server.
adb devices
— displays the list of connected devices where our lost emulator should appear.
For the laziest among you, an emulator can be created from the command line. For example, the following command creates an emulator named "test" using an x86 system image with API 25:):
avdmanager create avd —n test —k "system—images;android—25;google_apis;x86"
If the desired image is not available, you can pre-install it with the command:
sdkmanager --install "system—images;android—25;google_apis;x86"
sdkmanager --list | grep system—images
— displays a list of images available for download
With emulators, “phantom” problems also sometimes occur during operation, and one frequently used command that helps here is to “cold-boot” the emulator without pulling up the automatic snapshot. The snapshot made at the output will be as follows:
emulator @avdname —no—snapshot—load
Here are a few more useful switches to consider when starting the emulator:
-no-snapshot-save
— no automatic snapshot will be saved
-no-snapshot
— no snapshot will be downloaded or saved
If the emulator still doesn’t function properly, it can be cleared by using the switch that returns the emulator to its original state: -wipe-data
Snapshot creation is a very useful feature for saving the device's different states. Manually, this can be done through the emulator settings or by running this command:
adb emu avd snapshot save test
— saves the emulator’s state where test is the name of the snapshot to be stored on the device
emulator @avdname —snapshot—list
— runs our emulator named @avd and displays the snapshot list in the console
Next, you can load a previously saved snapshot by using this command:
adb emu avd snapshot load test
— where test is the name of a previously saved snapshot
adb emu avd snapshot delete test
— deletes the snapshot named test
It’s also possible to immediately run the emulator with the snapshot we need:
emulator @avdname -snapshot test
You can also use the pull
command to get a snapshot from the device:
adb emu avd snapshot pull test /Users/username/
Our emulator can be operated via the telnet
console. But first you have to install it. The easiest way to do that is through the brew
package manager, if you have it. If not, then it's time to find out what it is and how to use it. So, install telnet
by using the command: brew install telnet.
Next, run our emulator, and in another tab of the terminal, connect to it with the command: telnet localhost port,
Example:
telnet localhost 5554
— connects to our emulator which uses port 5554
After completing the command, we can do all sorts of useful stuff with our emulator, including working with geo (e.g., the command geo fix 40.748840 -73.984279
will set our desired location at the specified coordinates), the network or the battery, whereas a complete list of commands as always can be found via help.
For example, the same procedure with snapshots is somewhat simplified, while the commands outlined in the previous section are reduced to avd snapshot <command>
.
The window manager (wm) has some useful commands to make sure the elements on the screen of the device are displayed correctly. They let you adjust the pixel density resolution so you can go through all the necessary options for screen sizes and see how our app will adapt to them, without having the appropriate number of devices on hand:
adb shell wm size 1080x1920
— sets the custom screen resolution, with a width of 1080 and a height of 1920.
adb shell wm size reset
— resets all our changed settings.
adb shell wm density X
— changes the pixel density, where the minimum value is 72. The greater the value, the larger the elements on the screen.
adb shell wm density reset
— resets all our changed settings.
If we run our commands without any arguments, we will get back the current screen resolution and pixel density of the connected device/emulator.
Separately, we can mention the Monkey - a tool which generates random user events on the emulator or device, such as clicks, taps, and gestures, as well as a number of system-level events that resemble the movements of a silly monkey. The Monkey can be used for stress testing.
adb shell monkey
— displays all Monkey parameters.
Example of a complete scenario:
adb shell monkey ——throttle 100 ——pct—syskeys 0 —p com.myApp —v 10
The--throttle
key — sets the delay between actions in milliseconds. As the Monkey performs all the actions quickly, this switch (key) is usually used when we want to visually control what is happening on screen.
The --pct-syskeys
key — defines the percentage of system buttons that will be pressed during a scenario. In this example, it’s set to 0, which implies that no system buttons will be pressed.
The -p
switch — the name of the packet being transmitted
The -v
switch — the number of actions to be performed
Normally, the operations involved here imply revoking app permissions, because permissions are usually granted via an app request - something that can be done quickly and easily - whereas revoking permissions is done through system settings.
adb shell dumpsys package com.MyApp | grep permission
— displays a list of available application permissions, e.g., install permissions
— mandatory permissions that are granted when installing the application, runtime permissions
— permissions that are requested at a specific moment, e.g., when accessing the file storage. Note that if a permission is missing from the requested permission list, you will not be able to grant access to it.
So, the following command has to be run to revoke the permission of our application:
adb shell pm revoke packageName permissionName
, example:
adb shell pm revoke com.MyApp android.permission.CAMERA
— revokes the access of com.myApp
to the camera. Once we return to the application and try to use the camera through it, we will see a new request for permission.
The grant command gives permission to the application, for example:
adb shell pm grant com.myApp android.permission.CAMERA
— grants access to the phone's camera for our application.
Let's take a brief look here at the battery and standby mode.
adb shell dumpsys battery
— displays battery information.
adb shell dumpsys battery set level X
— sets the battery charge level, where X is the percentage of charge.
adb shell dumpsys battery unplug
— simulates a battery unplug.
adb shell dumpsys battery reset
— resets all our changed settings.
Now let's look at standby modes. Starting from Android 6 there is the function known as Doze Mode, for saving battery power and extending battery life by limiting app activity after the user has not interacted with the device for some time and when the device is not on charge.
The system periodically exits Doze Mode to complete pending background tasks. App Standby is another similar Android feature. Unlike Doze Mode, it tracks the state of a specific app that has been idle for a certain period of time and then activates standby mode. Our job is to make sure that the app recovers normally after exiting these two power saving modes, that it doesn’t crash, that notifications continue to come through, etc.
To switch the device to Doze Mode, run the following commands:
adb shell dumpsys battery unplug
— unplugs the battery .
adb shell dumpsys deviceidle step
— the command might have to be executed several times until is displayed: Stepped to deep: IDLE
.
After all the routines we have completed with the battery, it’s best to run the command:
adb shell dumpsys battery reset
— which returns it to its original state.
This command can also be used to force the device into Doze Mode:
adb shell dumpsys deviceidle force—idle
— sometimes, prior to this, you have to run the command: adb shell dumpsys deviceidle enable.
You can reactivate it back from Doze Mode with the command:
adb shell dumpsys deviceidle unforce
— and don't forget to reset the battery status:
adb shell dumpsys battery reset
.
Now a little bit about App Standby. To deploy the application in this mode, the following commands need to be run:
adb shell dumpsys battery unplug
— disconnects the battery as in the previous case.
adb shell am set—inactive com.myApp true
— deploys the application in App Standby mode.
Next, switch our application out of App Standby mode by using this command:
adb shell am set—inactive com.myApp false
.
You can check the app status by running this command:
adb shell am get—inactive com.myApp
adb reboot
— reboots the device (relevant for the real device as well).
adb shell dumpsys package com.myApp
— displays full information about a specific application.
adb shell dumpsys meminfo com.myApp
— to check out the memory usage of the application on the device, ranging from the space taken up to displaying the databases used by this app, as well as the path to them.
adb shell getprop
— shows a list of available device properties (manufacturer, device model, hardware specifications, etc.)
Display the list of activities that are accessible for the application:
adb shell dumpsys package com.myApp | grep —i Activity
.
Display the name of the running activity:
adb shell dumpsys window | grep Focused
.
Run the selected app activity:
adb shell am start —n com.myApp/.ActivityClass
— this way you can run any installed apps, including system applications. Example: adb shell am start -n com.android.settings/.Settings displays our phone settings.
Make a call to the specified phone number:
adb shell am start —a android.intent.action.CALL tel:+790900000XX
.
Open a page in your web browser:
adb shell am start —a android.intent.action.VIEW 'https://indriver.com'
I’d like to point out that it’s impossible to cram all the features of Android Debug Bridge into one article or conduct a thorough study of them. Changes are constantly occurring: what works today may suddenly stop working tomorrow, and the need for knowledge of certain tools will arise as we search for solutions to specific issues. But I can say with confidence that the material we have covered today is enough to get you started and then keep going for a while.
Good luck in your endeavors and I hope you enjoy diving into the exciting world of software testing!