One of the most painful things I found while learning Android development was handling orientation changes (who thought flipping your phone would cause so much trouble).
This is because the activities are being destroyed and recreated every time the phone is changing between portrait and landscape mode. Here I’m assuming that it is being handled ‘properly’ by saving state data (e.g. in onRetainNonConfigurationInstance() ) and then reloading it when the activity is created.
After this saving and reloading code is in place, then it needs to be unit tested, and here I’ve found Robotium to be very useful in running instrumentation tests with orientation changes.
An example
This is just one way that I’ve found useful, here is an example – take this simple test to simulate the user entering some text and clicking some buttons:
public void testValidateText() { EditText targetText = solo.getEditText(0); solo.enterText(targetText, "hello"); // click on button to bring up dialog solo.clickOnButton("Validate"); // wait to dialog to pop up getInstrumentation().waitForIdleSync(); // click on button in dialog solo.clickOnButton("OK"); }
For the sake of simplicity, I’ve left out the Robotium setup code and the asserts (which would normally be the point of the testing!).
Ring the (orientation) changes
In the next version of this test, I will:
- rename the test method to something that doesn’t start with ‘test’, because Android used JUnit 3, this will stop this method from being run in the test case
- add some parameters to the test method as flags for doing orientation change
- add to code in the method to change the orientation
- write some wrapper methods to call our test method
public void testValidateText() { doTestValidateText(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, false); } public void testValidateTextWithOrientationChange() { doTestValidateText(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, true); } public void testValidateTextInLandscapeWithOrientationChange() { solo.setActivityOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); doTestValidateText(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, true); } public void doTestValidateText(int currentOrientation, boolean changeOrientation) { int orientation = currentOrientation; EditText targetText = solo.getEditText(0); solo.enterText(targetText, "hello"); orientation = changeOrientation(solo, changeOrientation, orientation); // click on button to bring up dialog solo.clickOnButton("Validate"); orientation = changeOrientation(solo, changeOrientation, orientation); // wait to dialog to pop up getInstrumentation().waitForIdleSync(); orientation = changeOrientation(solo, changeOrientation, orientation); // click on button in dialog solo.clickOnButton("OK"); }
So now when the test case is run, the original test method will be run in 3 ways:
- testValidateText() will run the test as before
- testValidateTextWithOrientationChange() will run the test, but after every user action will change the orientation with the line
orientation = changeOrientation(solo, changeOrientation, orientation); - testValidateTextInLandscapeWithOrientationChange() will also run the test with orientation changes, but starting off in landscape mode
Here is the code for the orientation change.
public int getNextOrientation(int currentOrientation) { if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } else { return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } } public int changeOrientation(Solo solo, int currentOrientation) { int nextOrientation = getNextOrientation(currentOrientation); solo.setActivityOrientation(nextOrientation); return nextOrientation; } public int changeOrientation(Solo solo, boolean changeOrientation, int orientation) { int newOrientation = orientation; if (changeOrientation) { newOrientation = changeOrientation(solo, orientation); } return newOrientation; }
So basically the wrapper methods just invoke the original test method with a flag for the orientation that it starts in, and whether to do orientation changes during the test or not.
In the next post, I will list some problems in my code that I exposed with this testing, and other issues with coding and testing for orientation changes.