Reset app state between InstrumentationTestCase runs

Improving on @nenick's solution, encapsulate the state clearing behavior in a custom ActivityTestRule. If you do this, you can allow the test to continue to launch the activity automatically without intervention from you. With a custom ActivityTestRule, the activity is already in the desired state when it launches for the test.

Rules are particularly useful because they're not tied to any specific test class, so can be easily reused within any test class or any project.

Below is one I implemented to ensure that the app is signed out when the activity launches, per test. Some tests, when they failed, were leaving the app in a signed in state. This would then cause later tests to also fail because the later ones assumed they would need to sign in, but the app would already be signed in.

public class SignedOutActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
    
    public SignedOutActivityTestRule(Class<T> activityClass) {
        super(activityClass);
    }

    @Override
    protected void beforeActivityLaunched() {
        super.beforeActivityLaunched();
        InstrumentationRegistry.getTargetContext()
                .getSharedPreferences(
                        Authentication.SHARED_PREFERENCES_NAME,
                        Context.MODE_PRIVATE)
                .edit()
                .remove(Authentication.KEY_SECRET)
                .remove(Authentication.KEY_USER_ID)
                .apply();
    }

}

Current espresso doesn't provide any mechanism to reset application state. But for each aspect (pref, db, files, permissions) exist a solution.

Initial you must avoid that espresso starts your activity automatically so you have enough time to reset.

@Rule
public ActivityTestRule<Activity> activityTestRule = new ActivityTestRule<>(Activity.class, false, false);

And later start your activity with

activityTestRule.launchActivity(null)

For reseting preferences you can use following snippet (before starting your activity)

File root = InstrumentationRegistry.getTargetContext().getFilesDir().getParentFile();
String[] sharedPreferencesFileNames = new File(root, "shared_prefs").list();
for (String fileName : sharedPreferencesFileNames) {
    InstrumentationRegistry.getTargetContext().getSharedPreferences(fileName.replace(".xml", ""), Context.MODE_PRIVATE).edit().clear().commit();
}

You can reset preferences after starting your activity too. But then the activity may have already read the preferences.

Your application class is only started once and already started before you can reset preferences.

I have started to write an library which should make testing more simple with espresso and uiautomator. This includes tooling for reseting application data. https://github.com/nenick/espresso-macchiato See for example EspAppDataTool with the methods for clearing preferences, databases, cached files and stored files.


you can try add this to gradle:

android {
...
defaultConfig {
...
    testInstrumentationRunnerArguments clearPackageData: 'true'
   }
}

refer to https://developer.android.com/training/testing/junit-runner

To remove all shared state from your device's CPU and memory after each test, use the clearPackageData flag.