unit testing – How do you force a configuration change in an Android Robolectric test?-ThrowExceptions

Exception or error:

I’m using robolectric to make my android unit tests fast enough to be useful. I want to test that code I’ve written works while the screen orientation is changing to simulate a common real world use case.

Specifically what I’m testing is an asynchronous http call to a server with some xml parsed after the result is fetched. I have the unit test for all that working great but can’t figure out how to simulate the screen rotation. Any state change that causes the activity to recreate itself is fine, it doesn’t necessarily have to be screen rotation.

A solution that uses the emulator is not an option as I run my tests several times per minute and they must run under 2 seconds. I would also like this to work with roboguice if possible.

Thanks.

How to solve:

What Android API level are you compiling against? If it’s 3.0 or above you could try Activity.recreate(). The documentation states:

Cause this Activity to be recreated with a new instance. This results in essentially the same flow as when the Activity is created due to a configuration change — the current instance will go through its lifecycle to onDestroy() and a new instance then created after it.

Haven’t tried it myself though.

###

Calling recreate in Robolectric (note that you don’t have to worry about older API versions when using Robolectric) is pretty close to simulating a configuration change, but won’t necessarily catch all errors you could make. In particular it does not create a new instance of the Activity (and I’m pretty sure it does not ‘scrub’ it), so if you have forgotten to restore member fields of your Activity your tests won’t catch that. It does work well enough for testing fragments though (non retained fragments are destroyed and re-instantiated).

If you call recreate on an Activity in a Robolectric test the following happens:

  1. onSaveInstanceState
  2. onPause
  3. onStop
  4. onDestroy
  5. onCreate
  6. onStart
  7. onRestoreInstanceState
  8. onResume

(I found this out by overriding most lifecycle methods in a test activity and putting logging statements in them)

You can get a little closer to a real configuration change with code like the following:

Bundle bundle = new Bundle();
activityController.saveInstanceState(bundle).pause().stop().destroy();
controller = Robolectric.buildActivity(YourActivity.class).create(bundle).start().restoreInstanceState(bundle).resume();
activity = controller.get();

(This code is for Robolectric 2.1 – if you are on 2.2 or up, you will possibly want a .visible() call after that .resume())

Using the above you will see the following events occur:

  1. onSaveInstanceState
  2. onPause
  3. onStop
  4. onDestroy
  5. new instance of Activity instantiated (all following calls are on this new instance)
  6. onCreate
  7. onStart
  8. onRestoreInstanceState
  9. onResume
  10. onPostResume

This still isn’t an exact match but is much closer to what will happen when a real configuration change is encountered.

I think this might be a decent simulation of what happens when an activity is destroyed due to low memory, as unlike calling recreate() I don’t think this will keep hold of references to retained fragments. I’m on shaky ground in this area though!

Update:

If your Activity was started via an intent, you might need to add in a call to withIntent, like so:

Robolectric.buildActivity(YourActivity.class).withIntent(intent).create(bundle) // and so on...

###

I’ve had success using ZoFreX’s answer, however I’d like to add how to actually simulate the rotation. I know the OP specified that rotation is not an absolute must, but the title hints that this should be included in the answer, and could help people who end up straying here.

basically, set the orientation of the activity before applying ZoFrex’s solution. Or more concisely in code:

// toggle orientation
int currentOrientation = fragment.getActivity().getResources().getConfiguration().orientation;
boolean isPortraitOrUndefined = currentOrientation == Configuration.ORIENTATION_PORTRAIT || currentOrientation == Configuration.ORIENTATION_UNDEFINED;
int toOrientation = isPortraitOrUndefined ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
Robolectric.application.getResources().getConfiguration().orientation = toOrientation;

// ZoFreX's solution
Bundle bundle = new Bundle();
activityController.saveInstanceState(bundle).pause().stop().destroy();
controller = Robolectric.buildActivity(YourActivity.class).create(bundle).start().restoreInstanceState(bundle).resume();
activity = controller.get();

Please checkout ZoFreX’s solution because it contains additional info not included here.

###

The ActivityController class of Robolectric has a configurationChange() method that probably handles this. Hell, it even has a javadoc comment! 😀

Leave a Reply

Your email address will not be published. Required fields are marked *