I have a bunch of end-to-end instrumentation tests (relying on Espresso) that start our launcher activity, then navigate throughout our app (eventually creating several activities). At the end of each test our @After annotated tear down method performs some cleanup.
The problem we have is that after the test finishes (successful or failed assertion) the app is still "running", so some of the cleanup is actually causing the app to crash. This is either resulting in false positives if the assert was successful, or hiding the test failure (we only see the crash not the failed assertion).
Here's an example:
import android.app.Instrumentation;
import android.content.Intent;
import android.preference.PreferenceManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import com.example.SplashActivity;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
public class ExampleTest {
@Rule
public ActivityTestRule<SplashActivity> splashActivityTestRule
= new ActivityTestRule<>(SplashActivity.class, true, false);
Instrumentation.ActivityMonitor splashActivityMonitor;
@Before
public void setUp() {
splashActivityMonitor = new Instrumentation.ActivityMonitor(SplashActivity.class.getName(), null, false);
getInstrumentation().addMonitor(splashActivityMonitor);
}
@Test
public void someTest() throws Exception {
// ... other test-specific setup before starting splash activity
// start first activity
splashActivityTestRule.launchActivity(new Intent());
// a bunch of espresso steps that result in several other activities
// ... creating and adding Instrumentation.ActivityMonitor for each one
// assert something
}
@After
public void tearDown() {
// clear shared prefs to prepare for next test
PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getTargetContext())
.edit()
.clear()
.apply();
// At this point the app is still running. Maybe a UI is still loading that was not relevant to the test,
// or some mock web request is in flight. But at some point after the final assert in our test, the app needs
// to get something from shared prefs, which we just cleared, so the app crashes.
}
}
As you can see, the app is still running during the tear down method. Any changes we make to the state of the app here may cause the app to crash.
So how can I assert that the app is dead and gone before doing this cleanup?
Some possible (but ugly) solutions I've come up with:
After the final assert, continue to navigate back to some neutral point in the app (i.e use espresso to logout and return to splash screen). This should work but it'll add a lot of other steps to every test. Also I'm not sure this will work if the assert fails.
Or perform some sort of app kill in the teardown:
public void tearDown() {
// finish all tasks before cleaning up
ActivityManager activityManager =
(ActivityManager) InstrumentationRegistry.getTargetContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
for (ActivityManager.AppTask appTask : appTasks) {
appTask.finishAndRemoveTask();
}
// clear shared prefs to prepare for next test
PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getTargetContext())
.edit()
.clear()
.apply();
}
Update:
I know I can use ActivityTestRule.afterActivityFinished() docs but I don't think this will work for multiple actvities.