Introduction
The "App Shortcuts" feature allows users to access a specific part of the application from the device's home screen. Users can see all available shortcuts by long pressing on the icon of the applications.
There are 3 types of application shortcuts in Android:
- Static shortcuts are defined in a resource file that is packaged into an APK or app bundle.
- Dynamic shortcuts can be added, updated, and removed by your app only at runtime.
- Pinned shortcuts can be added to supported launchers at runtime, if the user grants permission.
More information about the app shortcuts feature can be found in this official documentation.
The dynamic shortcuts involve business logic of the application. If we want to be sure that we have proper shortcuts for our application, we should cover them with test cases. Usually, the user sees a default app shortcut (create message) and a few dynamic shortcuts. The user sees specific shortcuts based on recent activities (favorite music band, recently viewed profiles, and much more).
As you can see, we can have personalized shortcuts available for every user, and many of these use cases can be easily covered with UI tests.
What tools can you use for testing app shortcuts?
There are a few frameworks which allow us to test Android applications:
- UiAutomator
- Espresso
- Frameworks which use Espresso and/or UiAutomator inside (Kakao, Kaspresso, Appium, etc).
In our case, we need to interact not only with our application, but also with the launcher app (sometimes we don't need to interact with an app which we develop in UI tests). To have the possibility to interact with any application installed on the device (the launcher app), we need to use the UiAutomator or UiAutomator-based framework. The UiAutomator framework allows us to interact with components (such as notification and permissions) outside of our application.
You can read more about testing notifications here.
How to test app shortcuts?
To show the list of application shortcuts, we need to take the following steps:
- Navigate to the device's home screen.
- Open app applications.
- Find a specific application.
- Long press on an application icon.
After that, we will see the full list of app shortcuts. However, the default launcher can behave differently on another version of Android. Let's explore the difference between launchers on Android emulators with API 25 and API 31.
As you can see, the two launchers look differently. The launcher on the emulator with API 25 has a button, and after clicking on it, we will see a list of all the applications. This approach doesn't work on emulators with API 33 because we need to use a fling gesture from the bottom to the top to see all apps. In addition to that, launchers use different IDs of elements and we cannot have the same code for both launchers.
We can explore the view tree of different launchers use the "LayoutInspector" tool and selecting the launcher processes:
- The
com.android.launcher3
process for emulator with API 25 - The
com.google.android.apps.nexuslauncher
process for emulator with API 31
Remember to disable animations on devices/emulators, to avoid flakiness. You can read more about disabling animations here.
Let's create a function which will work on emulators with different API levels.
fun openAllApps(device: UiDevice) {
device.pressHome()
if (android.os.Build.VERSION.SDK_INT == 25) {
device.findObject(By.desc("Apps list"))
.click()
} else {
device.findObject(By.res("com.google.android.apps.nexuslauncher:id/workspace"))
.fling(Direction.DOWN)
}
}
You can use a similar approach for creating a function which works with all devices which you use for testing, as devices from different vendors can use different launchers.
The next step is to long press on the application icon and display application shortcuts.
fun openAppShortcuts(
device: UiDevice,
name: String
) {
device.findObject(By.desc(name))
.longClick()
}
Finally, we can verify that our application has all the required shortcuts.
fun verifyAppShortcuts(
device: UiDevice,
shortcuts: List<String>
) {
shortcuts.forEach { appShortcut ->
assertEquals(appShortcut, device.findObject(By.text(appShortcut)).text)
}
}
Let’s take a look at the test case, which includes all steps:
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = 25)
class AppShortcutsTest {
private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@Test
fun shouldDisplayStaticShortcutsForCalendarApp() {
openAllApps(device)
openAppShortcuts(
device,
"Calendar"
)
verifyAppShortcuts(
device,
listOf("New event", "New reminder")
)
}
@After
fun tearDown() {
device.pressHome()
}
}
When we test dynamic shortcuts, we can start our test case by running an application and taking steps which will trigger dynamic shortcuts, like opening a profile, sending a message, etc. Afterwards, we can start using the UiAutomator (or UiAutomator based framework) for pressing one the home screen, opening app shortcuts, and verifying them.
If you want to learn how to combine Espresso and UiAutomator in one test case, you can check the Espresso & UIAutomator together article.
Conclusion
Every user of our application might see different application shortcuts because some of them depend on user behavior. The UiAutomator (or UiAutomator based) framework allows us to easily verify application shortcuts.
When we want to verify app shortcuts, we need to interact not only with the application, but also with a launcher installed on the device/emulator. The default launcher for Android emulators can differ from version to version. In this article, we explored how we can handle this and potentially verify app shortcuts on different devices.
To verify dynamic shortcuts, we can start a test case by running an application and using a framework which simplifies this process, like Espresso/Kakao. After we have done everything inside the application, we can come back to the device's home screen and start interacting with a launcher using the UiAutomator (or UiAutomator based).