Testing UI Components

Typically, your Activity includes user interface components (such as buttons, editable text fields, checkboxes, and pickers) to allow users to interact with your Android application. This lesson shows how you can test an Activity with a simple push-button UI. You can use the same general steps to test other, more sophisticated types of UI components.

Note: The type of UI testing in this lesson is called white-box testing because you have the source code for the application that you want to test. The Android Instrumentation framework is suitable for creating white-box tests for UI components within an application. An alternative type of UI testing is black-box testing, where you may not have access to the application source. This type of testing is useful when you want to test how your app interacts with other apps or with the system. Black-box testing is not covered in this training. To learn more about how to perform black-box testing on your Android apps, see the UI Testing guide.

For a complete test case example, take a look at ClickFunActivityTest.java in the sample app.

Create a Test Case for UI Testing with Instrumentation

When testing an Activity that has a user interface (UI), the Activity under test runs in the UI thread. However, the test application itself runs in a separate thread in the same process as the application under test. This means that your test app can reference objects from the UI thread, but if it attempts to change properties on those objects or send events to the UI thread, you will usually get a WrongThreadException error.

To safely inject Intent objects into your Activity or run test methods on the UI thread, you can extend your test class to use ActivityInstrumentationTestCase2. To learn more about how to run test methods on the UI thread, see Testing on the UI thread.

Set Up Your Test Fixture

When setting up the test fixture for UI testing, you should specify the touch mode in your setUp() method. Setting the touch mode to true prevents the UI control from taking focus when you click it programmatically in the test method later (for example, a button UI will just fire its on-click listener). Make sure that you call setActivityInitialTouchMode() before calling getActivity().

For example:

public class ClickFunActivityTest
        extends ActivityInstrumentationTestCase2 {
    ...
    @Override
    protected void setUp() throws Exception {
        super.setUp();

        setActivityInitialTouchMode(true);

        mClickFunActivity = getActivity();
        mClickMeButton = (Button) 
                mClickFunActivity
                .findViewById(R.id.launch_next_activity_button);
        mInfoTextView = (TextView) 
                mClickFunActivity.findViewById(R.id.info_text_view);
    }
}

Add Test Methods to Validate UI Behavior

Your UI testing goals might include:

  • Verifying that a button is displayed with the correct layout when the Activity is launched.
  • Verifying that a TextView is initially hidden.
  • Verifying that a TextView displays the expected string when a button is pushed.

The following section demonstrates how you can implement test methods to perform these verifications.

Verify Button Layout Parameters

You might add a test method like this to verify that a button is displayed correctly in your Activity:

@MediumTest
public void testClickMeButton_layout() {
    final View decorView = mClickFunActivity.getWindow().getDecorView();

    ViewAsserts.assertOnScreen(decorView, mClickMeButton);

    final ViewGroup.LayoutParams layoutParams =
            mClickMeButton.getLayoutParams();
    assertNotNull(layoutParams);
    assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
    assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
}

In the assertOnScreen() method call, you should pass in the root view and the view that you are expecting to be present on the screen. If the expected view is not found in the root view, the assertion method throws an AssertionFailedError exception, otherwise the test passes.

You can also verify that the layout of a Button is correct by getting a reference to its ViewGroup.LayoutParams object, then call assertion methods to verify that the Button object's width and height attributes match the expected values.

The @MediumTest annotation specifies how the test is categorized, relative to its absolute execution time. To learn more about using test size annotations, see Apply Test Annotations.

Verify TextView Layout Parameters

You might add a test method like this to verify that a TextView initially appears hidden in your Activity:

@MediumTest
public void testInfoTextView_layout() {
    final View decorView = mClickFunActivity.getWindow().getDecorView();
    ViewAsserts.assertOnScreen(decorView, mInfoTextView);
    assertTrue(View.GONE == mInfoTextView.getVisibility());
}

You can call getDecorView() to get a reference to the decor view for the Activity. The decor view is the top-level ViewGroup (FrameLayout) view in the layout hierarchy.

Verify Button Behavior

You can use a test method like this to verify that a TextView becomes visible when a Button is pushed:

@MediumTest
public void testClickMeButton_clickButtonAndExpectInfoText() {
    String expectedInfoText = mClickFunActivity.getString(R.string.info_text);
    TouchUtils.clickView(this, mClickMeButton);
    assertTrue(View.VISIBLE == mInfoTextView.getVisibility());
    assertEquals(expectedInfoText, mInfoTextView.getText());
}

To programmatically click a Button in your test, call clickView(). You must pass in a reference to the test case that is being run and a reference to the Button to manipulate.

Note: The TouchUtils helper class provides convenience methods for simulating touch interactions with your application. You can use these methods to simulate clicking, tapping, and dragging of Views or the application screen.

Caution: The TouchUtils methods are designed to send events to the UI thread safely from the test thread. You should not run TouchUtils directly in the UI thread or any test method annotated with @UIThread. Doing so might raise the WrongThreadException.

Apply Test Annotations

The following annotations can be applied to indicate the size of a test method:

@SmallTest
Marks a test that should run as part of the small tests.
@MediumTest
Marks a test that should run as part of the medium tests.
@LargeTest
Marks a test that should run as part of the large tests.

Typically, a short running test that take only a few milliseconds should be marked as a @SmallTest. Longer running tests (100 milliseconds or more) are usually marked as @MediumTests or @LargeTests, depending on whether the test accesses resources on the local system only or remote resources over a network. For guidance on using test size annotations, see this Android Tools Protip.

You can mark up your test methods with other test annotations to control how the tests are organized and run. For more information on other annotations, see the Annotation class reference.