167 lines
6.3 KiB
Plaintext
167 lines
6.3 KiB
Plaintext
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>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|