Home | History | Annotate | Download | only in activity-testing
      1 page.title=Creating Functional Tests
      2 trainingnavtop=true
      3 @jd:body
      4 
      5 <!-- This is the training bar -->
      6 <div id="tb-wrapper">
      7 <div id="tb">
      8 
      9 <h2>This lesson teaches you to</h2>
     10 <ol>
     11    <li><a href="#test_methods">Add Test Method to Validate Functional Behavior</a>
     12    <ol>
     13       <li><a href="#activitymonitor">Set Up an ActivityMonitor</a></li>
     14       <li><a href="#keyinput">Send Keyboard Input Using Instrumentation</a></li>
     15    </ol>
     16    </li>
     17 </ol>
     18 
     19 <h2>Try it out</h2>
     20 <div class="download-box">
     21  <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip"
     22 class="button">Download the demo</a>
     23  <p class="filename">AndroidTestingFun.zip</p>
     24 </div>
     25 
     26 </div>
     27 </div>
     28 <p>Functional testing involves verifying that individual application
     29 components work together as expected by the user. For example, you can create a
     30 functional test to verify that an {@link android.app.Activity} correctly
     31 launches a target {@link android.app.Activity} when the user performs a UI
     32 interaction.</p>
     33 
     34 <p>To create a functional test for your {@link android.app.Activity}, your test
     35 class should extend {@link android.test.ActivityInstrumentationTestCase2}.
     36 Unlike {@link android.test.ActivityUnitTestCase},
     37 tests in {@link android.test.ActivityInstrumentationTestCase2} can
     38 communicate with the Android system and send keyboard input and click events to
     39 the UI.</p>
     40 
     41 <p>For a complete test case example, take a look at
     42 {@code SenderActivityTest.java} in the sample app.</p>
     43 
     44 <h2 id="test_methods">Add Test Method to Validate Functional Behavior</h2>
     45 <p id="test_goals">Your functional testing goals might include:</p>
     46 <ul>
     47 <li>Verifying that a target {@link android.app.Activity} is started when a
     48 UI control is pushed in the sender {@link android.app.Activity}.</li>
     49 <li>Verifying that the target {@link android.app.Activity} displays the
     50 correct data based on the user's input in the sender
     51 {@link android.app.Activity}.</li>
     52 </ul>
     53 <p>You might implement your test method like this:</p>
     54 
     55 <pre>
     56 &#64;MediumTest
     57 public void testSendMessageToReceiverActivity() {
     58     final Button sendToReceiverButton = (Button) 
     59             mSenderActivity.findViewById(R.id.send_message_button);
     60 
     61     final EditText senderMessageEditText = (EditText) 
     62             mSenderActivity.findViewById(R.id.message_input_edit_text);
     63 
     64     // Set up an ActivityMonitor
     65     ...
     66 
     67     // Send string input value
     68     ...
     69 
     70     // Validate that ReceiverActivity is started
     71     ...
     72 
     73     // Validate that ReceiverActivity has the correct data
     74     ...
     75 
     76     // Remove the ActivityMonitor
     77     ...
     78 }
     79 </pre>
     80 <p>The test waits for an {@link android.app.Activity} that matches this monitor,
     81 otherwise returns null after a timeout elapses. If {@code ReceiverActivity} was
     82 started, the {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}
     83 that you set
     84 up earlier receives a hit. You can use the assertion methods to verify that
     85 the {@code ReceiverActivity} is indeed started, and that the hit count on the
     86 {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} incremented
     87 as expected.</p>
     88 
     89 <h2 id="activitymonitor">Set up an ActivityMonitor</h2>
     90 <p>To monitor a single {@link android.app.Activity} in your application, you
     91 can register an {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}.
     92 The {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} is
     93 notified by the system whenever an {@link android.app.Activity} that matches your criteria is started.
     94 If a match is found, the monitors hit count is updated.</p>
     95 <p>Generally, to use an
     96 {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}, you should:</p>
     97 <ol>
     98 <li>Retrieve the {@link android.app.Instrumentation} instance for your test
     99 case by using the
    100 {@link android.test.InstrumentationTestCase#getInstrumentation()} method.</li>
    101 <li>Add an instance of {@link android.app.Instrumentation.ActivityMonitor} to
    102 the current instrumentation using one of the {@link android.app.Instrumentation}
    103 {@code addMonitor()} methods. The match criteria can be specified as an
    104 {@link android.content.IntentFilter} or a class name string.</li>
    105 <li>Wait for the {@link android.app.Activity} to start.</li>
    106 <li>Verify that the monitor hits were incremented.</li>
    107 <li>Remove the monitor.</li>
    108 </ol>
    109 <p>For example:</p>
    110 <pre>
    111 // Set up an ActivityMonitor
    112 ActivityMonitor receiverActivityMonitor =
    113         getInstrumentation().addMonitor(ReceiverActivity.class.getName(),
    114         null, false);
    115 
    116 // Validate that ReceiverActivity is started
    117 TouchUtils.clickView(this, sendToReceiverButton);
    118 ReceiverActivity receiverActivity = (ReceiverActivity) 
    119         receiverActivityMonitor.waitForActivityWithTimeout(TIMEOUT_IN_MS);
    120 assertNotNull("ReceiverActivity is null", receiverActivity);
    121 assertEquals("Monitor for ReceiverActivity has not been called",
    122         1, receiverActivityMonitor.getHits());
    123 assertEquals("Activity is of wrong type",
    124         ReceiverActivity.class, receiverActivity.getClass());
    125 
    126 // Remove the ActivityMonitor
    127 getInstrumentation().removeMonitor(receiverActivityMonitor);
    128 </pre>
    129 
    130 <h2 id="keyinput">Send Keyboard Input Using Instrumentation</h2>
    131 <p>If your {@link android.app.Activity} has an {@link android.widget.EditText}
    132 field, you might want to test that users can enter values into the
    133 {@link android.widget.EditText} object.</p>
    134 <p>Generally, to send a string input value to an {@link android.widget.EditText}
    135 object in {@link android.test.ActivityInstrumentationTestCase2}, you should:</p>
    136 <ol>
    137 <li>Use the {@link android.app.Instrumentation#runOnMainSync(java.lang.Runnable) runOnMainSync()}
    138 method to run the {@link android.view.View#requestFocus()} call synchronously
    139 in a loop. This way, the UI thread is blocked until focus is received.</li>
    140 <li>Call {@link android.app.Instrumentation#waitForIdleSync()} method to wait
    141 for the main thread to become idle (that is, have no more events to process).</li>
    142 <li>Send a text string to the {@link android.widget.EditText} by calling
    143 {@link android.app.Instrumentation#sendStringSync(java.lang.String)
    144 sendStringSync()} and pass your input string as the parameter.</p>
    145 </ol>
    146 <p>For example:</p>
    147 <pre>
    148 // Send string input value
    149 getInstrumentation().runOnMainSync(new Runnable() {
    150     &#64;Override
    151     public void run() {
    152         senderMessageEditText.requestFocus();
    153     }
    154 });
    155 getInstrumentation().waitForIdleSync();
    156 getInstrumentation().sendStringSync("Hello Android!");
    157 getInstrumentation().waitForIdleSync();
    158 </pre>
    159 
    160 
    161 
    162 
    163 
    164 
    165 
    166 
    167