Nowadays, most Android applications support multiple locales, and many request different system permissions, for example permission to access the device camera, location permissions, and permission to write to external storage.
With the evolution of the Android platform, the approach to application permissions has changed in favor of user privacy. Starting from API level 23, application permissions are asked during application runtime and upon user requests. Such permissions are represented by a system popup or system dialog and are not the part of the application being tested. Moreover, these permissions can be revoked by the users any time from the application settings. Of course, the mentioned application states should be handled properly before or during UI tests are run.
This chapter explains the different ways we can deal with system actions like permission request dialogs and describes the possible solutions for changing the Android emulator system language programmatically.
Changing the Emulator System Language Programmatically
Note
Renaming the snapshot in the emulator Extended Controls window will not rename it globally, but just creates the alias. The actual snapshot name will remain unchanged.
The same can be done by communicating with the Android emulator via the telnet console command (more information about the emulator telnet command can be found at https://developer.android.com/studio/run/emulator-console ). In the following code examples, you can see how to establish the telnet session with a running emulator, list existing snapshots, take the snapshot, and load it during emulator runtime.
Since the main topic of this book is test automation, the following Python script creates the telnet connection to the localhost port 5554 (which is the first port the Android emulator takes when it is created) and loads the previously saved snapshot.
Alternatively, you can do the same thing using the expect scripting utility (for MacOS and UNIX users only).
Mac: brew install expect
UNIX/Linux: yum install expect
In general, the current snapshot approach works not only for setting emulator language but also gives us the ability to have different snapshots for many use cases, which you can come up with by your own.
Exercise 21
- 1.
Launch an emulator and save a couple of emulator snapshots with the help of emulator Extended Controls window. Load the snapshots manually.
- 2.
Launch an emulator using the console commands. Connect the telnet session to the emulator and save a couple of emulator snapshots. Load the snapshots from the console.
- 3.
Create a Python script with emulator telnet commands and run it. Observe the result.
- 4.
Install the expect utility on your computer, and then create and execute the script with the expect telnet commands. Observe the results.
Handling Runtime Permissions
Another burning topic in Android test automation related to system popups are runtime permissions. Appropriate permission should be requested by the Android application when it requires resources or information outside of its sandbox. The application declares permissions in the AndroidManifest.xml file and then requests that the user approve each permission at runtime (on Android 6.0 and higher).
When the user triggers a piece of code that requires additional permissions, the prompt shown by the system describes the permission group your app needs access to, not the specific permission.
In order to showcase this functionality, our sample application requests permission when we are adding an image to the TO-DO item. Try it out.
Enabling Permissions Using the GrantPermissionRule
Now let’s take a look at the RuntimePermissionTest.kt class , which contains the GrantPermissionRule sample. The GrantPermissionRule rule grants runtime permissions on Android M (API 23) and above. This rule is used when a test requires a runtime permission to do its work. When applied to a test class, this rule attempts to grant all requested runtime permissions. The requested permissions will then be granted on the device and will take immediate effect.
Clicking on the camera icon in the sample TO-DO application triggers the Camera permission prompt to be shown to the user. The prompt belongs to the different application package, called com.andriod.packageinstaller, which Espresso cannot interact with. So, in order to reduce external dependencies and keep our Espresso tests hermetic, GrantPermissionRule can be used to start the test with the already granted permission.
UI tests remain hermetic and do not require interactions with other system services.
Permission is granted for each test case inside the test class.
It is not possible to test different runtime permission use cases like getting permission after denial or trying to use the feature without permission granted.
There is no way to revoke a permission after it is granted. Attempting to do so will crash the instrumentation process.
In general, using GrantPermissionRule is a nice way to grant runtime permissions and avoid permission dialogs from showing up and blocking the application UI. From the other side, as was stated, it limits us in terms of covering multiple runtime permission requests use cases that are also part of the application that should be tested.
Handling Runtime Permissions Using UI Automator
Another way to handle runtime permissions is to use the UI Automator test framework functionality together with Espresso. Since it allows us to interact with any application, we are able to perform UI actions on permission dialogs as well.
Camera permissions granted the first time the permission dialog is shown to the user.
Camera permission denied by the user during the first occurrence, but then the user realizes that she needs such functionality and enables it when the permission dialog is presented a second time.
Permission is denied two times. The second denial was made with the Don’t Ask Again checkbox checked. The user tries to use the Camera feature again, but now she must manually enable it from the application permission settings.
And just to be clear, there is an application code-behind all four use cases, which in the case of using GrantPermissionRule , are not covered by automated tests and require manual testing.
Second, we may face an issue using only AndroidJUnitRunner for permission tests. The thing is that each test requires a clean application state without granted permissions. Therefore, the option with the Android Test Orchestrator described in Chapter 1 should be used with testInstrumentationRunnerArguments clearPackageData: 'true' parameter (see app/build.gradle file for more details). It ensures that each test will be run within its own invocation, including the cleaned application permissions state.
Third, we have to inspect all areas we will navigate to with the Monitor tool, making the UI dump and collect identifiers from elements used in defined use cases.
Allow button—com.android.packageinstaller:id/permission_allow_button
Deny button —com.android.packageinstaller:id/permission_deny_button
Don’t Ask Again checkbox—com.android.packageinstaller:id/do_not_ask_checkbox
Permissions list item—Fourth item in the recycler view com.android.settings:id/list
Camera permission list item—Zeroth element in the list view android:id/list
The first use case is represented by the takesCameraPicture() test case , where the user clicks just once on the permission dialog.
The second use case is covered by the deniesAndGrantsPermission() test .
In the third use case, it gets a bit more complicated since we have to interact with the settings application. Here goes the test.
Where sendApplicationSettingsIntent() is responsible for creating and firing an intent to show the TO-DO application settings page.
Then enableCameraPermission() contains the code to open the application’s permission settings and click on the camera permission item (see Figures 9-4 and 9-5).
Finally, launchBackToDoApplication() sends an intent to launch the sample application.
Exercise 22
- 1.
Delete GrantPermissionRule from RuntimePermissionsTest.kt and run a test. Observe the results. Revert the GrantPermissionRule deletion and run the test again. Observe the results.
- 2.
Run all tests implemented in the RuntimePermissionsUiAutomatorTest.kt class. Remove any Android Test Orchestrator dependencies from the application build.gradle file and run the test again. Observe the results. Revert to the original file.
- 3.
Write a test that opens the TO-DO application settings and enables camera permission. Then open the TO-DO application and proceed with task creation.
Summary
This chapter showed how to change the Android emulator system language at runtime and described different ways of handling runtime permissions in UI tests. It should be clear that multi-language support is a must-have for modern Android applications. This requires thorough testing and enabling the test environment to switch emulator system languages easily is an essential part of this test infrastructure.
Having an easy and reliable way to set runtime permissions is a crucial and sensitive topic for the end users. It impacts user satisfaction and should be thoroughly tested. Applications should handle different permission flows properly and without mistakes. Of course, it is up to you to select which testing approach works best for your specific case—using the GrantPermissionRule or fully automating the permission granting with the UI Automator framework. With the knowledge from this chapter, you can do that easily.