Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop and restart an activity in an android instrumentation test?

I'm trying to write an Android activity instrumentation test that stops (onPause(), then onStop()) and restarts the current activity. I tried

activity.finish();
activity = getActivity();

...but that doesn't seem to work properly.

The goal of the test is to assert that form data is stored during the onPause() method and re-read during the onStart() method. It works when doing it manually, but the test fails, from which I draw the conclusion that activity.finish() seems to be the wrong way to stop and restart an activity.


Edit: My main problem seems to have been a synchronization issue. After restarting the activity, the test runner didn't wait for all event handlers to finish. The following line halts the test execution until the activity is idle:

getInstrumentation().waitForIdleSync()

Besides that, take a look at the accepted answer for more valuable information about the lifecycle.

like image 767
Danilo Bargen Avatar asked Apr 24 '12 10:04

Danilo Bargen


People also ask

How do you test activity?

To test an activity, you use the ActivityTestRule class provided by the Android Testing Support Library. This rule provides functional testing of a single activity. The activity under test will be launched before each test annotated with @Test and before any method annotated with @Before.

What are instrumentation tests in android?

Note: Instrumented test, also known as instrumentation tests, are initialized in a special environment that gives them access to an instance of Instrumentation. This class provides access to the application context and APIs to manipulate the app under test and gives instrumented tests their name.

How do you get activity from ActivityScenarioRule?

ActivityScenarioRule launches a given activity before the test starts and closes after the test. You can access the ActivityScenario instance via getScenario() . You may finish your activity manually in your test, it will not cause any problems and this rule does nothing after the test in such cases.


3 Answers

By calling (or trigger a screen orientation change):

activity.finish(); // old activity instance is destroyed and shut down.
activity = getActivity(); // new activity instance is launched and created.

Causing the activity go through the complete recreation life cycle:

onPause() -> onStop() -> onDestroy() -> onCreate()

What you need is:

onPause() -> onStop() -> onRestart()

I exposed the Instrumentation API recently and found plenty of interesting activity life cycle trigger method callActivityOnXXX(), the following single line of code should do the tricky:

MyActivity myActivity = getActivity();
// make activity falling into restart phase:
getInstrumentation().callActivityOnRestart(myActivity);

Activity life cycle diagram quoting from official dev guide: enter image description here

like image 63
yorkw Avatar answered Nov 11 '22 08:11

yorkw


I tried calling .finish(), setActivity(null), getActivity() and it does restart the activity, but for me it was not restoring the state. I tried out all the other answers on SO, and every other method to do this I could find online, and none of them worked for me. After much experimentation I found the following works (nb: requires API level 11+):

    getInstrumentation().runOnMainSync(new Runnable() {
        @Override
        public void run() {
            activity.recreate();
        }
    });
    setActivity(null);
    activity = getActivity();

When I do this a new Activity instance is created, and a new instance of the fragment I had attached to the activity earlier in the test is also created, and both the activity and fragment restore their state in the expected manner.

I don't know how this works or why this works, I reached this solution through trial and error, and I have only tested it on a Nexus 4 running KitKat. I can't guarantee it correctly simulates an activity recreation, but it worked for my purposes.

Edit: At a later date I figured out how this works. getActivity() works through registering hooks that receive new Activities being created, which catch the new Activity created by activity.recreate(). setActivity(null) was required to clear the internal cache backing getActivity, otherwise it will return the old one and not look for a new one.

You can see how this works from examining the source code for the various test case classes one extends from.

like image 35
ZoFreX Avatar answered Nov 11 '22 09:11

ZoFreX


A good way to test lifecycle events is through screen orientation changes. In my experience it's a convenient way to bombproof the onPause / onStart pattern.

like image 28
Joel Skrepnek Avatar answered Nov 11 '22 09:11

Joel Skrepnek