by Tim Lavers
(Test. Crash? Code.)+
Thats my preferred workflow as a programmer. So when I started writing a simple Android
application my first task was to get this productive cycle happening. This was not completely
straightforward. In fact Im not finished yet. But Ive made enough progress that I think that its
worth writing down what Ive found so far, for two reasons. Firstly, Ive got a working system that
others can use and can learn from. Secondly, I hope that readers can point out problems with my
approach and suggest alternatives.
By a functional test I mean a program that runs the application as a user would and checks that
some particular requirement is implemented correctly. In general, these tests use more than a single
screen of the application.
In this article we will look at the ui proxy pattern allows for a fluid programming style in writing
functional tests. We will also look at the code that is needed in order to allow a sequence of
independent tests to be run as an ant task. Finally, we will discuss the Robotium toolkit, which is
great for simulating user actions in tests.
There are three screens, or Activities: a WordListDisplay for showing the words, a
WordListSelecter for choosing a list of words to work on, and a ChapterSelecter for choosing
a collection of word lists. The underlying Android application is an instance of a class called
IndoFlash. This class manages the state of the system, such as which list is selected and what is in
the list of favourites.
UI Proxy Classes
In the code shown above, the object ui is a test proxy for the WordListDisplay, which is the
main application Activity. The class of ui is UIWordListDisplay. Every user operation of the
WordListDisplay is matched by a method in this class.
The call openFavouritesList(); midway through the test is an example of a multi-screen
operation. It is implemented as follows:
protected void openFavouritesList() {
UIWordListSelecter uiWordListSelecter = ui.showWordLists();
ui = uiWordListSelecter.selectFavourites();
}
We see here that there is also a test proxy class for WordListSelecter, which is the Activity that is
used to switch between different wordlists. There is also a ui proxy, UIChapterSelecter, for the
chapter selection activity.
It is the use of ui proxy classes that allows the fluent programming style in writing tests. This pattern
can be used for pretty much any user interface platform. In the book Swing Extreme Testing it is
used to test desktop applications.
The user actions that are performed in the function tests, such as clicking a button or scrolling, make
use of the TouchUtils class of the android.test package. For example, to select the favourites
list, we need to do some scrolling:
public UIWordListDisplay selectFavourites() {
TouchUtils.scrollToBottom(testCase(), activity(), listView());
return selectList(listView().getChildCount() - 1);
}
Within the tests for IndoFlash this is implemented as the launch() method of
UIWordListDisplay:
public void launch() {
Instrumentation.ActivityMonitor monitor =
instrumentation().addMonitor(WordListDisplay.class.getName(), null, false);
setMonitor(monitor);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(instrumentation().getTargetContext(),
WordListDisplay.class.getName());
instrumentation().startActivitySync(intent);
Activity activity =
instrumentation().waitForMonitorWithTimeout(monitor(), 5);
setActivity(activity);
}
Each of the function tests used in the development of IndoFlash extends from FunctionTest,
which provides this basic test structure:
//Launch the main activity, which is WordListDisplay
//Run the specific test, which is the doIt() method of subclasses.
//Get the application back to a known state.
Most of the code is in getting the application, which is an instance of IndoFlash, back to its initial
state: showing the first word list in the first chapter, with the English translation first and the word
list unshuffled. The pause at the very end of the test is annoying, but without it the tests are flaky.
So far there are 16 function tests and the entire build-test cycle takes almost 7 minutes on a fairly
fast laptop. Of course, 80 seconds of that 7 minutes is caused by the 5 second pause at the end of
each test an example of how evil these pauses are.
Robotium
In a code snippet earlier we saw how TouchUtils can be used to perform simple actions such as
pressing buttons and scrolling through lists. To write tests that involve more complex actions it is
best to make use of the Robotium tool. Robotium has a rich API for simulating user actions and also
has very useful methods for investigating the state of the application.
Lets see an example of a Robotium test. One of the requirements of IndoFlash is that the action bar
always shows a help button and an information button. When these are pressed, message windows
should show that display the appropriate information. For example:
UIActivity has a solo() method that returns a Robotium Solo object, which is the workhorse
of the Robotium API. This can be used to check that the help button shows the correct information:
ui.solo().clickOnActionBarItem(R.id.action_help);
ui.solo().waitForDialogToOpen();
Assert.assertTrue(checkThatAlertDialogShows("How to use IndoFlash",
"Press the Show button to see"));
ui.solo().clickOnButton("OK");
ui.solo().waitForDialogToClose();
The trickiest part of this test is the method checkThatAlertDialogShows. This is why. The
IndoFlash code that creates this dialog uses the AlertDialog.Builder class:
Our difficulty is that in the resulting View object, we dont know the id of either of the important
TextView instances. However, with the help of Robotium, we can track them down:
boolean checkThatAlertDialogShows(@NotNull CharSequence title, @NotNull
CharSequence messageStart) {
boolean titleFound = false;
boolean messageFound = false;
ArrayList<View> currentViews = ui.solo().getCurrentViews();
for (View view : currentViews) {
if (view instanceof TextView) {
CharSequence text = ((TextView) view).getText();
if (title.equals(text)) {
titleFound = true;
} else if (isPrefixFor(messageStart, text)) {
messageFound = true;
}
}
}
return titleFound && messageFound;
}
private boolean isPrefixFor(CharSequence pref, CharSequence text) {
if (pref.length() > text.length()) return false;
CharSequence sub = text.subSequence(0, pref.length());
return sub.equals(pref);
}
In a more complex application, in which there were more tests involving ActionDialog, it might be
worth refactoring this kind of code into a UIActionDialog class.
Summary
This article has presented code for running suites multi-screen functional tests of an Android
application. The tests are written in a very fluid style, thanks to the use of the ui proxy design
pattern. By harnessing the power of Robotium, it is possible to write tests that are even more
expressive and powerful. The source code for IndoFlash and its tests are an excellent starting point
for anyone implementing multi-screen functional tests for their Android applications.
Resources
The code for IndoFlash and its tests can be downloaded from here.
The Robotium tool is available from Google code: https://code.google.com/p/robotium/
The book Android in Practice: http://www.manning.com/collins/ contains a good overview of
Android testing.
This excellent article contained some code snippets that helped me get started with multi-screen
tests: http://www.vogella.com/tutorials/AndroidTesting/article.html
I have tried both the Eclipse and IntelliJ IDEs for developing Android applications and have a strong
preference for IntelliJ.