So far you have learned how to locate Views, write your own test from scratch, and how to automate a RecyclerView. During these chapters and exercises we have been moving from black-box to white-box. In the first two exercises you had to interact with Views that are visible in the UI, and during the third we accessed the RecyclerView adapter to interact with items in the RecyclerView that were not available through the UI. In this chapter we will continue by using some of the app’s code to manipulate its state.

Compartmentalized Tests

UI tests can take many forms: sometimes you will want to script a user journey through the app, and at other times you will just want to test a specific feature or screen in the app. That last type of UI test can suffer a little if you have to navigate through several screens before you get to that one screen you actually care about.

In the test app we have experienced exactly that scenario: every time we launch the app we have to dismiss the onboarding to get to the feature we are testing.

To solve that problem we will have to make sure that the app doesn’t launch the onboarding. We are going to do that by editing the app’s SharedPreferences after the app has been installed on our test device, but before our Activity is launched. But first I will explain what SharedPreferences are, how we can edit them, and finally how to do that at the right time.

SharedPreferences

SharedPreferences are a small collection of key-value pairs, that are stored on the device and used to store user and app settings.

In case of the test app that means that we use the SharedPreferences to store a Boolean value called “is_first_launch”. By default it is set to ‘true’, and this value is read every time the app is launched. If the value is true, the app will launch the onboarding. Once the onboarding has been dismissed the value is set to ‘false’. The next time the app is launched it will see the value ‘false’ and skip the onboarding.

Editing the SharedPreferences

For an example of how to edit the SharedPreferences we need to look no further than the test app. The app has a class called SharedPreferencesUtil that contains the following code:

private val IS_FIRST_LAUNCH = "is_first_launch"

fun setIsFirstLaunchToFalse(context: Context) {
val editor = context.getSharedPreferences(context.packageName, Activity.MODE_PRIVATE).edit()
editor.putBoolean(IS_FIRST_LAUNCH, false)
editor.commit()
}

This app is written in Kotlin code, so to make things a bit more familiar I will show you the Java equivalent:

private String IS_FIRST_LAUNCH = "is_first_launch";

public void setIsFirstLaunchToFalse(Context context) {
    SharedPreferences.editor editor = context.getSharedPreferences(context.packageName, Activity.MODE_PRIVATE).edit();
    editor.putBoolean(IS_FIRST_LAUNCH, false);
    editor.commit();
}

What’s happening here is that we access the SharedPreferences and set it to edit mode. This returns an editor object that we save in a variable called ‘editor’. Using the editor we can put a key-value pair to the shared preferences and commit our changes.

A closer examination of the TestRule

The TestRule installs and then launches our application for us. In between these two steps we would like to edit the SharesPreferences. This means we need to take a closer look at what the TestRule actually does.

If you take a closer look at the ActivityTestRule class (CMD+click, or CTRL+click the class name in Android Studio), you will see that it actually contains several methods that it calls when we invoke our TestRule. These methods are:

  • beforeActivityLaunched()
  • afterActivityLaunched()
  • afterActivityFinished()
  • getActivityIntent()

If we override one of these methods, we can tell our ActivityTestRule to perform our own code during that step.

Overriding beforeActivityLaunched()

To override beforeActivityLaunched we will have to adjust our TestRule code a little bit:

 @Rule
 public ActivityTestRule<MainActivity> activityTestRule =
     new ActivityTestRule<MainActivity>(MainActivity.class) {
     
     @Override
     public void beforeActivityLaunched() {
          super.beforeActivityLaunched();
          // code we'd like to execute to edit SharedPreferences
      }
 }; 

We are creating a new instantiation of the ActivityTestRule, but as you can see we are adding curly brackets containing our ‘override code’, just like we would add a body to a Class (this is called an anonymous inner Class).

The ‘@Override’ annotation signifies that this method override the original implementation. In the method itself we call ‘super.beforeActivityLaunched()’. This executes the code from the original beforeActivityLaunched method. And after that we are free to execute our own code.

Exercise 4: Breaking the Rules!

Time put all this together: Implement te new version of our TestRule, so we can override beforeActivityLaunched. Then in that method edit the SharedPreferences and we should be good to go.

If you think you know what to do, go right ahead. If you’ve got no idea, then I will give you a bit more code and a link to the solution.

The TestRule we already have just needs to be replaced with example I gave in the previous section. The harder part is actually accessing the SharedPreferences and editing them.

What we need is the editor, and we can get that with the following code:

SharedPreferences.Editor editor = context.getSharedPreferences(context.getPackageName(), Activity.MODE_PRIVATE).edit(); 

As you can see, I’m supplying an argument named ‘context.getPackageName()’. The package name of the app is used in order to find the right SharedPreferences (for this app). You can get the context using the following code:

Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 

If you add this code to the ‘beforeActivityLaunched’ method (context first, then the SharedPreferences editor), then all that is left is editing the SharedPreferences using the editor. For an example of that, take a look at the examples from the application that I provided at the start of this chapter. And if you’re still can’t get it to work, you can take a look at the solution and replicate that. Good Luck!

The solution can be found at GitHub:
Solution Exercise 4

Continue with Part 6: White box automation: Intents