Testing for Android with Robotium

Android’s built-in testing framework is lacking on a number of levels. Enter Robotium, an open-source project. Robotium connects the dots by extending Android’s testing framework and providing convenience methods to help developers rapidly create tests.

In this entry I will show how Robotium can be used for data input, finding and testing views and buttons, testing between multiple activities, and testing activities with extras. I’ll also show how to work around issues with race-conditions in UI tests. I have created a GitHub project with a sample application and test project.

Getting Started

To get started with Robotium, you create a standard ‘Android Test Project’ in Eclipse, and then including the Robotium JAR in your Java build path. You can then create a test class by extending ActivityInstrumentationTestCase2:

public class MainTest extends ActivityInstrumentationTestCase2<Main> {
  ...
}

Once this is done you will have access to both the standard testing framework included with Android and also Robotium. Specifically, you create a Solo object as a member variable which gets initialized in setUp():

@Override
protected void setUp() throws Exception {
	super.setUp();
	solo = new Solo(getInstrumentation(), getActivity());
}

So now let’s actually write the test. The most essential thing when it comes to testing an Android application is getting access to our views and being able to touch them or change their values. While this is possible with instrumentation, doing so tends to get the developer thinking more about how they are going to manipulate the testing framework to achieve a particular end, rather than just focussing on getting the job done.

Robotium helps get around this by providing helper methods. So if we wanted to, for example, click on some text, it’d be as simple as this:

solo.clickOnText(“Text we want to click on”);

Clicking text is cool, but sometimes we can’t locate a view by searching for text. In such cases it’s necessary to locate it by ID. We do this just as we’d do it in the application itself, but we use the Solo object to get it:

final EditText exampleEditText = 
  (EditText) solo.getCurrentActivity().findViewById(R.id.example_edit_text);

Once we have gotten access to the text view, we can enter text into it as if we were a user:

solo.enterText(exampleEditText, “hello world”);

All we have to do is pass in the view and the text and Robotium does the rest for us.

Of course, all of this would be for nothing if we weren’t actually checking anything. This is where we link back to Android’s instrumentation. Say we had a read-only text view that was mirroring the contents of the editable text box. We’d first locate it using:

final TextView exampleTextView = 
  (TextView) solo.getCurrentActivity().findViewById(R.id.example_text_view);

and then check it’s visibility and contents using:

assertEquals(View.VISIBLE, exampleTextView.getVisibility());
assertEquals("hello world", exampleTextView.getText().toString());

Activities

So now we know how to find views, manipulate them, and check them. Now we can do some end-to-end testing between multiple activities.

Normally, checking what the current activity is would get kind of hairy using instrumentation. However, Robotium makes it simple:

solo.assertCurrentActivity("<error message>", NewActivity.class);

As with everything else that Robotium provides, we can now express our test steps in a way that clearly maps to what the user will experience.

Conversely, we don’t always want to be launching our application from scratch for testing. Sometimes we just want to test a specific activity which could be launched with many different extras, and thus will behave differently. And we don’t always want to run through the entire application every time to get to this point, because it can be very time consuming.

We achieve this by overriding getActivity() in our test and setting our extras accordingly:

@Override
public NewActivity getActivity() {
	Intent intent = new Intent();
	intent.putExtra("exampleExtra", "some data");
	setActivityIntent(intent);
	return super.getActivity();
}

This is particularly useful for basic validation tests.

Race Conditions

A common problem with automated UI testing is race conditions. Unfortunately for us, Android is no different.

The main culprit for race conditions is animations. Take the following animation code:

TextView example06TextView = (TextView)findViewById(R.id.example_06_text_view);

example06TextView.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(final View animatedView) {
		Animation fadeOut = new AlphaAnimation(1, 0);
		fadeOut.setInterpolator(new AccelerateInterpolator());
		fadeOut.setDuration(1000);
		fadeOut.setAnimationListener(new AnimationListener() {
			@Override
			public void onAnimationStart(Animation animation) {}

			@Override
			public void onAnimationRepeat(Animation animation) {}

			@Override
			public void onAnimationEnd(Animation animation) {
				animatedView.setVisibility(View.GONE);
			}
		});
		animatedView.startAnimation(fadeOut);
	}
});

This will set up an animation that fades-out a text view until it disappears. Once the animation is finished the view will be gone from the layout (which means the parent layout will no longer measure it). The animation is set to take 1000ms to complete.

Unfortunately there is no pretty way to handle test this. However, a quick and simple way is to write a helper class that periodically checks to see if some wait-condition has been met:

public class TestUtil {
    private static final int WAIT_TIME = 500;
    private static final int MAX_ATTEMPTS = 6;

	public static void tryUntil(Wait wait) {
        for (int i = 0; i < MAX_ATTEMPTS; i++) {
            wait.run();
            if (wait.finishedWaiting) {
                return;
            } else {
                try {
                    Thread.sleep(WAIT_TIME);
                } catch (InterruptedException e) { }
            }
        }
    }

    public static abstract class Wait implements Runnable {
        public boolean finishedWaiting;
    }
}

The last thing the animation is going to do is set the the view visibility to View.GONE. To test for this, we can utilize TestUtil.tryUntil() as follows:

@Smoke
public void testExample06() {
	final TextView example06TextView = 
          (TextView) solo.getCurrentActivity().findViewById(R.id.example_06_text_view);

	assertEquals(View.VISIBLE, example06TextView.getVisibility());
	solo.clickOnView(example06TextView);

	TestUtil.tryUntil(new Wait() {
		@Override
		public void run() {
			finishedWaiting = example06TextView.getVisibility() == View.GONE;
		}
	});

	assertEquals(View.GONE, example06TextView.getVisibility());
}

Conclusion

So now that I’ve covered some of the basics of Robotium, let’s do a quick overview of some of its pros and cons:

Pros:

  1. Insanely easy to setup and begin testing
  2. The application you test is the application that will be submitted to Google Play, you can even test plain .apk files
  3. Can be integrated with continuous integration tools such as Jenkins
  4. Can test on both devices and emulators
  5. Tests written in Java

Cons:

  1. Can be a bit slow, especially running on older devices (that said, it runs crazy fast on my Galaxy SIII)
  2. Tests written in Java

On balance, I think Robotium is definitely worthwhile using. The biggest issue in automated UI testing is race conditions, which any framework would experience and for which there are easy solutions.

You’ll notice that I have listed Java as both a pro and a con – while it is great for devs to stay using the same language, it’s a much larger barrier-to-entry for business-type folks.

As has happened on other mobile platforms, Android applications are evolving from simple toy-apps into feature-rich applications – some of which could even serve as PC replacements. Therefore it is only logical that they should go through the exact same quality-assurance processes that all other software is put through. Robotium is a great framework for making this process as effortless as possible.

About Tim Osmond

Shine Technologies, Graduate Developer
This entry was posted in Android, Java, Testing, Tools, UI. Bookmark the permalink.

5 Responses to Testing for Android with Robotium

  1. Marc Fasel says:

    To experience Robotium testing live in action come see Tim Osmond at the upcoming Android Australia Meetup at Shine Headquarters:

    http://www.meetup.com/Android-Australia-User-Group-Melbourne/events/87356702/

  2. Rasmikanta Guru says:

    hi,

    I am using robotium for the automation android device.
    I am trying to click on french language using solo.clickOnText(“Français”).

    When language name is hard coded it is working, but when i am accessing from xml or properties file it is showing the error i.e. shown in below
    The code is like: solo.clickOnText(ExcelInsertion.getContent(“lang”));

    error :
    junit.framework.AssertionFailedError: No TextView with text Fran?ais is found!
    at com.jayway.android.robotium.solo.Getter.getView(Getter.java:71)
    at com.jayway.android.robotium.solo.Solo.getText(Solo.java:1434)
    at com.testtelus.TestTelus.testCheckA01_UI_03(TestTelus.java:90)

    It is because of special chracter “ç”.
    But while it is printing with System.out.println(ExcelInsertion.getContent(“lang”)). It is showing perfect.

    Is their any way to solve this?

  3. Abhishek Sharma says:

    HI,
    I have just started with Robotium and feels its really a good tool. Only thing what I don’t understand is from here methods like testCanOpenSetting() or testBlackbox or etc, is called . Can you explain a bit from where these methods are called or there is some other way to write the method so that they are automatically called.

  4. Moumita Ghosh says:

    Hi!! I am trying to automate mobile application but I am unable to click on Action Bar that is Sherlock Action Bar.Please tell the solution to click on menus in Sherlock Action Bar.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s