Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to automatically test onResume behaviour by calling onDestroy using Robotium?

I am using Robotium to functionally test an Android app. I'd like to test the onResume behaviour after onDestroy has been called. This post hints on using Instrumentation, but i can't get it to work. I have included the following, but this fails with an IllegalStateException. Is it possible to destroy the app and restart it?

public class MainActivityFunctionalTest extends ActivityInstrumentationTestCase2<MainActivity> {
private Solo solo;

public MainActivityFunctionalTest() {
    super(MainActivity.class);
}

public void testActionList() {
    getInstrumentation().callActivityOnDestroy(solo.getCurrentActivity());
    ...
}

Results in the following exception:

java.lang.IllegalStateException: Must be called from main thread of process at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1373) at android.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:1825) at android.app.Activity.performDestroy(Activity.java:5171) at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1109) at nl.handypages.trviewer.test.MainActivityFunctionalTest.testActionList(MainActivityFunctionalTest.java:81) at java.lang.reflect.Method.invokeNative(Native Method) at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214) at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199) at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175) at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)
like image 732
Bram Avatar asked Feb 17 '13 20:02

Bram


4 Answers

You can simulate a configuration change like this:

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

This will result in a new instance of the activity being created, and it should go through the lifecycle correctly (including passing the bundle to restore from to the new activity). Note that this only works in Honeycomb (API level 11) and higher.

like image 24
ZoFreX Avatar answered Oct 02 '22 11:10

ZoFreX


Well you have two issues here, the first issue is that the instrumentation callactivityondestroy methods require you to call it from the main thread. see runOnUiThread usage tips for details on how to do this.

The second issue is that (and i might be wrong here) is never suppose to go from onDestroy to onResume http://developer.android.com/reference/android/app/Activity.html says that after ondestroy the activity is literally destroyed. You would have to create a new instance of the activity to get another onResume. with you could do by launching your activity again. I suppose you could do it your way but im not sure how valid it would be, if you wanted to do it in such a way, just call the onResume in the same way as onDestroy as noted in the question i linked to.

like image 33
Paul Harris Avatar answered Oct 02 '22 13:10

Paul Harris


I don't think you can resume an activity that is already destroyed. If your activity is not destroyed, but only stopped, then you can restart and resume it. OnResume needs to be called on a UI thread, so suppose you want to test the resume state after the activity is already running, you can do this:

@UiThreadTest
public void testResumeAfterStop() {
    Instrumentation instr = this.getInstrumentation();

    // here, test or record down whatever should be tested 
    // when the activity is in resume state the first time

    instr.callActivityOnPause(getActivity());
    instr.callActivityOnStop(getActivity());
    instr.callActivityOnRestart(getActivity());
    instr.callActivityOnStart(getActivity());
    instr.callActivityOnResume(getActivity());

    // Now you are in the resume state again. 
    // Test whatever you need here.

}
like image 181
Ahui Avatar answered Oct 02 '22 11:10

Ahui


Thanks. I added and called this.

public void callActivityOnDestroy(final Activity activity){
    getInstrumentation().runOnMainSync(new Runnable() {
            public void run() {
                activity.finish();
            }
        });
}
public void callActivityStart(final Activity activity){
    getInstrumentation().runOnMainSync(new Runnable() {
            public void run() {
                activity.startActivity(new Intent(activity, MainActivity.class));
            }
        });
}

And alternatively (in my case) i can just call (if i don't want to complete kill the process):

getInstrumentation().callActivityOnStop(getActivity());
getInstrumentation().callActivityOnRestart(getActivity());
like image 43
Bram Avatar answered Oct 02 '22 12:10

Bram