page.title=Creating Functional Tests trainingnavtop=true @jd:body <!-- This is the training bar --> <div id="tb-wrapper"> <div id="tb"> <h2>This lesson teaches you to</h2> <ol> <li><a href="#test_methods">Add Test Method to Validate Functional Behavior</a> <ol> <li><a href="#activitymonitor">Set Up an ActivityMonitor</a></li> <li><a href="#keyinput">Send Keyboard Input Using Instrumentation</a></li> </ol> </li> </ol> <h2>Try it out</h2> <div class="download-box"> <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip" class="button">Download the demo</a> <p class="filename">AndroidTestingFun.zip</p> </div> </div> </div> <p>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 {@link android.app.Activity} correctly launches a target {@link android.app.Activity} when the user performs a UI interaction.</p> <p>To create a functional test for your {@link android.app.Activity}, your test class should extend {@link android.test.ActivityInstrumentationTestCase2}. Unlike {@link android.test.ActivityUnitTestCase}, tests in {@link android.test.ActivityInstrumentationTestCase2} can communicate with the Android system and send keyboard input and click events to the UI.</p> <p>For a complete test case example, take a look at {@code SenderActivityTest.java} in the sample app.</p> <h2 id="test_methods">Add Test Method to Validate Functional Behavior</h2> <p id="test_goals">Your functional testing goals might include:</p> <ul> <li>Verifying that a target {@link android.app.Activity} is started when a UI control is pushed in the sender {@link android.app.Activity}.</li> <li>Verifying that the target {@link android.app.Activity} displays the correct data based on the user's input in the sender {@link android.app.Activity}.</li> </ul> <p>You might implement your test method like this:</p> <pre> @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 ... } </pre> <p>The test waits for an {@link android.app.Activity} that matches this monitor, otherwise returns null after a timeout elapses. If {@code ReceiverActivity} was started, the {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} that you set up earlier receives a hit. You can use the assertion methods to verify that the {@code ReceiverActivity} is indeed started, and that the hit count on the {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} incremented as expected.</p> <h2 id="activitymonitor">Set up an ActivityMonitor</h2> <p>To monitor a single {@link android.app.Activity} in your application, you can register an {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}. The {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} is notified by the system whenever an {@link android.app.Activity} that matches your criteria is started. If a match is found, the monitor’s hit count is updated.</p> <p>Generally, to use an {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}, you should:</p> <ol> <li>Retrieve the {@link android.app.Instrumentation} instance for your test case by using the {@link android.test.InstrumentationTestCase#getInstrumentation()} method.</li> <li>Add an instance of {@link android.app.Instrumentation.ActivityMonitor} to the current instrumentation using one of the {@link android.app.Instrumentation} {@code addMonitor()} methods. The match criteria can be specified as an {@link android.content.IntentFilter} or a class name string.</li> <li>Wait for the {@link android.app.Activity} to start.</li> <li>Verify that the monitor hits were incremented.</li> <li>Remove the monitor.</li> </ol> <p>For example:</p> <pre> // 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); </pre> <h2 id="keyinput">Send Keyboard Input Using Instrumentation</h2> <p>If your {@link android.app.Activity} has an {@link android.widget.EditText} field, you might want to test that users can enter values into the {@link android.widget.EditText} object.</p> <p>Generally, to send a string input value to an {@link android.widget.EditText} object in {@link android.test.ActivityInstrumentationTestCase2}, you should:</p> <ol> <li>Use the {@link android.app.Instrumentation#runOnMainSync(java.lang.Runnable) runOnMainSync()} method to run the {@link android.view.View#requestFocus()} call synchronously in a loop. This way, the UI thread is blocked until focus is received.</li> <li>Call {@link android.app.Instrumentation#waitForIdleSync()} method to wait for the main thread to become idle (that is, have no more events to process).</li> <li>Send a text string to the {@link android.widget.EditText} by calling {@link android.app.Instrumentation#sendStringSync(java.lang.String) sendStringSync()} and pass your input string as the parameter.</p> </ol> <p>For example:</p> <pre> // Send string input value getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { senderMessageEditText.requestFocus(); } }); getInstrumentation().waitForIdleSync(); getInstrumentation().sendStringSync("Hello Android!"); getInstrumentation().waitForIdleSync(); </pre>