Creating Functional Tests

Functional testing involves verifying that individual application components work together as expected by the user. For example, you can create a functional test to verify that an Activity correctly launches a target Activity when the user performs a UI interaction.

To create a functional test for your Activity, your test class should extend ActivityInstrumentationTestCase2. Unlike ActivityUnitTestCase, tests in ActivityInstrumentationTestCase2 can communicate with the Android system and send keyboard input and click events to the UI.

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

Add Test Method to Validate Functional Behavior

Your functional testing goals might include:

  • Verifying that a target Activity is started when a UI control is pushed in the sender Activity.
  • Verifying that the target Activity displays the correct data based on the user's input in the sender Activity.

You might implement your test method like this:

@MediumTest
public void testSendMessageToReceiverActivity() {
    final Button sendToReceiverButton = (Button) 
            mSenderActivity.findViewById(R.id.send_message_button);

    final EditText senderMessageEditText = (EditText) 
            mSenderActivity.findViewById(R.id.message_input_edit_text);

    // Set up an ActivityMonitor
    ...

    // Send string input value
    ...

    // Validate that ReceiverActivity is started
    ...

    // Validate that ReceiverActivity has the correct data
    ...

    // Remove the ActivityMonitor
    ...
}

The test waits for an Activity that matches this monitor, otherwise returns null after a timeout elapses. If ReceiverActivity was started, the ActivityMonitor that you set up earlier receives a hit. You can use the assertion methods to verify that the ReceiverActivity is indeed started, and that the hit count on the ActivityMonitor incremented as expected.

Set up an ActivityMonitor

To monitor a single Activity in your application, you can register an ActivityMonitor. The ActivityMonitor is notified by the system whenever an Activity that matches your criteria is started. If a match is found, the monitor’s hit count is updated.

Generally, to use an ActivityMonitor, you should:

  1. Retrieve the Instrumentation instance for your test case by using the getInstrumentation() method.
  2. Add an instance of Instrumentation.ActivityMonitor to the current instrumentation using one of the Instrumentation addMonitor() methods. The match criteria can be specified as an IntentFilter or a class name string.
  3. Wait for the Activity to start.
  4. Verify that the monitor hits were incremented.
  5. Remove the monitor.

For example:

// Set up an ActivityMonitor
ActivityMonitor receiverActivityMonitor =
        getInstrumentation().addMonitor(ReceiverActivity.class.getName(),
        null, false);

// Validate that ReceiverActivity is started
TouchUtils.clickView(this, sendToReceiverButton);
ReceiverActivity receiverActivity = (ReceiverActivity) 
        receiverActivityMonitor.waitForActivityWithTimeout(TIMEOUT_IN_MS);
assertNotNull("ReceiverActivity is null", receiverActivity);
assertEquals("Monitor for ReceiverActivity has not been called",
        1, receiverActivityMonitor.getHits());
assertEquals("Activity is of wrong type",
        ReceiverActivity.class, receiverActivity.getClass());

// Remove the ActivityMonitor
getInstrumentation().removeMonitor(receiverActivityMonitor);

Send Keyboard Input Using Instrumentation

If your Activity has an EditText field, you might want to test that users can enter values into the EditText object.

Generally, to send a string input value to an EditText object in ActivityInstrumentationTestCase2, you should:

  1. Use the runOnMainSync() method to run the requestFocus() call synchronously in a loop. This way, the UI thread is blocked until focus is received.
  2. Call waitForIdleSync() method to wait for the main thread to become idle (that is, have no more events to process).
  3. Send a text string to the EditText by calling sendStringSync() and pass your input string as the parameter.

For example:

// Send string input value
getInstrumentation().runOnMainSync(new Runnable() {
    @Override
    public void run() {
        senderMessageEditText.requestFocus();
    }
});
getInstrumentation().waitForIdleSync();
getInstrumentation().sendStringSync("Hello Android!");
getInstrumentation().waitForIdleSync();