Android-Developer-Tools

Activity Testing

The following sections have tips for testing the UI of your Android application, specifically to help you handle actions that run in the UI thread, touch screen and keyboard events, and home screen unlock during testing.

Testing on the UI thread

An application's activities run on the application's UI thread. Once the UI is instantiated, for example in the activity's onCreate() method, then all interactions with the UI must run in the UI thread. When you run the application normally, it has access to the thread and does not have to do anything special.

This changes when you run tests against the application. With instrumentation-based classes, you can invoke methods against the UI of the application under test. The other test classes don't allow this. To run an entire test method on the UI thread, you can annotate the thread with @UiThreadTest. Notice that this will run all of the method statements on the UI thread. Methods that do not interact with the UI are not allowed; for example, you can't invoke Instrumentation.waitForIdleSync().

To run a subset of a test method on the UI thread, create an anonymous class of type Runnable, put the statements you want in the run() method, and instantiate a new instance of the class as a parameter to the method appActivity.runOnUiThread(), where appActivity is the instance of the application you are testing.

For example, this code instantiates an activity to test, requests focus (a UI action) for the Spinner displayed by the activity, and then sends a key to it. Notice that the calls to waitForIdleSync and sendKeys aren't allowed to run on the UI thread:

  private MyActivity mActivity; // MyActivity is the class name of the app under test
  private Spinner mSpinner;

  ...

  protected void setUp() throws Exception {
      super.setUp();
      mInstrumentation = getInstrumentation();

      mActivity = getActivity(); // get a references to the app under test

      /*
       * Get a reference to the main widget of the app under test, a Spinner
       */
      mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);

  ...

  public void aTest() {
      /*
       * request focus for the Spinner, so that the test can send key events to it
       * This request must be run on the UI thread. To do this, use the runOnUiThread method
       * and pass it a Runnable that contains a call to requestFocus on the Spinner.
       */
      mActivity.runOnUiThread(new Runnable() {
          public void run() {
              mSpinner.requestFocus();
          }
      });

      mInstrumentation.waitForIdleSync();

      this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);

Turning off touch mode

To control the emulator or a device with key events you send from your tests, you must turn off touch mode. If you do not do this, the key events are ignored.

To turn off touch mode, you invoke ActivityInstrumentationTestCase2.setActivityTouchMode(false) before you call getActivity() to start the activity. You must invoke the method in a test method that is not running on the UI thread. For this reason, you can't invoke the touch mode method from a test method that is annotated with @UIThread. Instead, invoke the touch mode method from setUp().

Unlocking the emulator or device

You may find that UI tests don't work if the emulator's or device's home screen is disabled with the keyguard pattern. This is because the application under test can't receive key events sent by sendKeys(). The best way to avoid this is to start your emulator or device first and then disable the keyguard for the home screen.

You can also explicitly disable the keyguard. To do this, you need to add a permission in the manifest file (AndroidManifest.xml) and then disable the keyguard in your application under test. Note, though, that you either have to remove this before you publish your application, or you have to disable it with code in the published application.

To add the permission, add the element as a child of the element. To disable the KeyGuard, add the following code to the onCreate() method of activities you intend to test:

mKeyGuardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); mLock = mKeyGuardManager.newKeyguardLock("activity_classname"); mLock.disableKeyguard(); where activity_classname is the class name of the activity.

Troubleshooting UI tests

This section lists some of the common test failures you may encounter in UI testing, and their causes:

WrongThreadException

Problem:

For a failed test, the Failure Trace contains the following error message: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. Probable Cause:

This error is common if you tried to send UI events to the UI thread from outside the UI thread. This commonly happens if you send UI events from the test application, but you don't use the @UIThread annotation or the runOnUiThread() method. The test method tried to interact with the UI outside the UI thread. Suggested Resolution:

Run the interaction on the UI thread. Use a test class that provides instrumentation. See the previous section Testing on the UI Thread for more details.

java.lang.RuntimeException

Problem:

For a failed test, the Failure Trace contains the following error message: java.lang.RuntimeException: This method can not be called from the main application thread Probable Cause:

This error is common if your test method is annotated with @UiThreadTest but then tries to do something outside the UI thread or tries to invoke runOnUiThread(). Suggested Resolution:

Remove the @UiThreadTest annotation, remove the runOnUiThread() call, or re-factor your tests.