How do you force a configuration change in an Android Robolectric test?

davidjnelson picture davidjnelson · May 19, 2011 · Viewed 7.2k times · Source

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.

Answer

ZoFreX picture ZoFreX · Feb 17, 2014

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...