Android includes powerful tools for testing applications. The tools extend JUnit with additional features, provide convenience classes for mock Android system objects, and use
instrumentation to give you control over your main application while you are testing it. The entire Android testing environment is discussed in the document
This tutorial demonstrates the Android testing tools by presenting a simple Android application and then leading you step-by-step through the creation of a test application for it.
The test application demonstrates these key points:
</p>
<ul>
<li>
An Android test is itself an Android application that is linked to the application under test by entries in its <code>AndroidManifest.xml</code> file.
</li>
<li>
Instead of Android components, an Android test application contains one or more test cases. Each of these is a separate class definition.
</li>
<li>
Android test case classes extend the JUnit {@link junit.framework.TestCase} class.
</li>
<li>
Android test case classes for activities extend JUnit and also connect you to the application under test with instrumentation. You can send keystroke or touch events directly to the UI.
</li>
<li>
You choose an Android test case class based on the type of component (application, activity, content provider, or service) you are testing.
</li>
<li>
Additional test tools in Eclipse/ADT provide integrated support for creating test applications, running them, and viewing the results.
</li>
</ul>
<p>
The test application contains methods that perform the following tests:
</p>
<ul>
<li>
Initial conditions test. Tests that the application under test initializes correctly. This is also a unit test of the application's
{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method. Testing initial conditions also provides a confidence measure for subsequent tests.
</li>
<li>
UI test. Tests that the main UI operation works correctly. This test demonstrates the instrumentation features available in activity testing.
It shows that you can automate UI tests by sending key events from the test application to the main application.
</li>
<li>
State management tests. Test the application's code for saving state. This test demonstrates the instrumentation features of the test runner, which
are available for testing any component.
</li>
</ul>
<h2 id="Prerequisites">Prerequisites</h2>
<p>
The instructions and code in this tutorial depend on the following:
As a test of the AVD and emulator, run the SpinnerActivity application in Eclipse with ADT. When it starts,
click the large downward-pointing arrow to the right of the spinner text. You see the spinner expand and display the title "Select a planet" at the top.
Click one of the other planets. The spinner closes, and your selection appears below it on the screen.
</p>
<h2 id="SetupProjects">Setting Up the Projects</h2>
<p>
When you are ready to get started with the tutorial, begin by setting up Eclipse projects for
both Spinner (the application under test) and SpinnerTest (the test application).
</p>
<p>
You'll be using the Spinner application as-is, without modification, so you'll be loading it
into Eclipse as a new Android project from existing source. In the process, you'll be
creating a new test project associated with Spinner that will contain the SpinnerTest
application. The SpinnerTest application will be completely new and you'll be
using the code examples in this tutorial to add test classes and tests to it.
</p>
<p>
To install the Spinner app in a new Android project from existing source, following these steps:
</p>
<ol>
<li>
In Eclipse, select <strong>File</strong> > <strong>New</strong> > <strong>Project</strong> > <strong>Android</strong> > <strong>Android Project</strong>,
then click Next. The <strong>New Android Project</strong> dialog appears.
</li>
<li>
In the <em>Project name</em> text box, enter "SpinnerActivity". The <em>Properties</em> area is filled in automatically.
</li>
<li>
In the <em>Contents</em> area, set "Create project from existing source".
</li>
<li>
For <em>Location</em>, click <strong>Browse</strong>, navigate to the directory <code><SDK_path>/samples/android-8/Spinner</code>,
then click Open. The directory name <code><SDK_path>/samples/android-8/Spinner</code> now appears in the <em>Location</em> text box.
</li>
<li>
In the <em>Build Target</em> area, set a API level of 3 or higher. If you are already developing with a particular target, and it is API level 3 or higher, then use that target.
</li>
<li>
In the <em>Properties</em> area, in the <em>Min SDK Version:</em>, enter "3".
<img src="{@docRoot}images/testing/eclipse_new_android_testproject_complete_callouts.png" alt="New Android Test Project dialog with filled-in values" style="height:230px"/>
</a>
</li>
<li>
Click Finish. Entries for SpinnerActivity and SpinnerActivityTest should appear in the
<strong>Package Explorer</strong>.
<p class="note">
<strong>Note:</strong> If you set <em>Build Target</em> to an API level higher than "3", you will see the warning
"The API level for the selected SDK target does not match the Min SDK version". You do not need to change the API level or the Min SDK version.
The message tells you that you are building the projects with one particular API level, but specifying that a lower API level is required. This may
occur if you have chosen not to install the optional earlier API levels.
</p>
<p>
If you see errors listed in the <strong>Problems</strong> pane at the bottom of the Eclipse window, or if a red error marker appears next to
the entry for SpinnerActivity in the Package Explorer, highlight the SpinnerActivity entry and then select
<strong>Project</strong> > <strong>Clean</strong>. This should fix any errors.
</p>
</li>
</ol>
<p>
You now have the application under test in the SpinnerActivity project,
and an empty test project in SpinnerActivityTest. You may
notice that the two projects are in different directories, but Eclipse with
ADT handles this automatically. You should have no problem in either building or running them.
</p>
<p>
Notice that Eclipse and ADT have already done some initial setup for your test application.
Expand the SpinnerActivityTest project, and notice that it already has an
Notice the <code><instrumentation></code> element. The attribute
<code>android:targetPackage="com.android.example.spinner"</code> tells Android that the
application under test is defined in the Android package
<code>com.android.example.spinner</code>. Android now knows to use that
package's <code>AndroidManifest.xml</code> file to launch the application under test.
The <code><instrumentation></code> element also contains the attribute
<code>android:name="android.test.InstrumentationTestRunner"</code>, which tells Android
instrumentation to run the test application with Android's instrumentation-enabled test runner.
</p>
<h2 id="CreateTestCaseClass">Creating the Test Case Class</h2>
<p>
You now have a test project SpinnerActivityTest, and the basic structure of a test
application also called SpinnerActivityTest. The basic structure includes all the files and
directories you need to build and run a test application, except for the class that
contains your tests (the test case class).
</p>
<p>
The next step is to define the test case class. In this tutorial, you'll be creating a
test case class that includes:
</p>
<ul>
<li>
Test setup. This use of the JUnit {@link junit.framework.TestCase#setUp() setUp()}
method demonstrates some of the tasks you might perform before running an Android test.
</li>
<li>
Testing initial conditions. This test demonstrates a good testing technique.
It also demonstrates that with Android instrumentation you can look at the application
under test <em>before</em> the main activity starts. The test checks that the application's
important objects have been initialized.
If the test fails, you then know that any other tests against the application are
unreliable, since the application was running in an incorrect state.
<p class="note">
<strong>Note:</strong> The purpose of testing initial conditions is not the same as
using <code>setUp()</code>. The JUnit {@link junit.framework.TestCase#setUp()} runs once
before <strong>each test method</strong>, and its purpose is to create a clean test
environment. The initial conditions test runs once, and its purpose is to verify that the
application under test is ready to be tested.
</p>
</li>
<li>
Testing the UI. This test shows how to control the main application's UI
with instrumentation, a powerful automation feature of Android testing.
</li>
<li>
Testing state management. This test shows some techniques for testing how
well the application maintains state in the Android environment. Remember that to
provide a satisfactory user experience, your application must never lose its current state,
even if it's interrupted by a phone call or destroyed because of memory constraints.
The Android activity lifecycle provides ways to maintain state, and the
<code>SpinnerActivity</code> application uses them. The test shows the techniques for
verifying that they work.
</li>
</ul>
<p>
Android tests are contained in a special type of Android application that contains one or more test class definitions. Each of these contains
one or more test methods that do the actual tests. In this tutorial, you will first add a test case class, and then add tests to it.
</p>
<p>
You first choose an Android test case class to extend. You choose from the base test case classes according to the Android component you are testing and the types of tests you are doing.
In this tutorial, the application under test has a single simple activity, so the test case class will be for an Activity component. Android offers several, but the one that tests in
the most realistic environment is {@link android.test.ActivityInstrumentationTestCase2}, so you will use it as the base class. Like all activity test case classes,
<code>ActivityInstrumentationTestCase2</code> offers convenience methods for interacting directly with the UI of the application under test.
</p>
<h3 id="AddTestCaseClass">Adding the test case class file</h3>
<p>
To add <code>ActivityInstrumentationTestCase2</code> as the base test case class, follow these steps:
</p>
<ol>
<li>
In the Package Explorer, expand the test project SpinnerActivityTest if it is not open already.
</li>
<li>
Within SpinnerActivityTest, expand the <code>src/</code> folder and then the package marker for
<code>com.android.example.spinner.test</code>. Right-click on the package name and select <strong>New</strong> > <strong>Class</strong>:<br/>
<img alt="Menu for creating a new class in the test application" src="{@docRoot}images/testing/spinner_create_test_class_callouts.png" style="height:230px"/>
</a>
<p>
The <strong>New Java Class</strong> wizard appears:
<img alt="New Java Class wizard dialog" src="{@docRoot}images/testing/spinnertest_new_class_callouts.png" style="height:230px"/>
</a>
</li>
<li>
In the wizard, enter the following:
<ul>
<li>
<em>Name:</em> "SpinnerActivityTest". This becomes the name of your test class.
</li>
<li>
<em>Superclass:</em> "<code>android.test.ActivityInstrumentationTestCase2<SpinnerActivity></code>". The superclass is parameterized, so
you have to provide it your main application's class name.
</li>
</ul>
<p>
Do not change any of the other settings. Click Finish.
</p>
</li>
<li>
You now have a new file <code>SpinnerActivityTest.java</code> in the project.
</li>
<li>
To resolve the reference to SpinnerActivity, add the following import:
} // end of SpinnerActivityTest constructor definition
</pre>
<p>
This calls the superclass constructor with the Android package name (<code>com.android.example.spinner</code>)and main activity's class
(<code>SpinnerActivity.class</code>) for the application under test. Android uses this information to find the application and activity to test.
</p>
<p>
You are now ready to add tests, by adding test methods to the class.
</p>
<h3 id="AddSetupMethod">Adding the setup method</h3>
<p>
The <code>setUp()</code> method is invoked before every test. You use it to initialize variables and clean up from previous tests. You can also use
the JUnit {@link junit.framework.TestCase#tearDown() tearDown()} method, which runs <strong>after</strong> every test method. The tutorial does not use it.
</p>
<p>
The method you are going to add does the following:
</p>
<ul>
<li>
<code>super.setUp()</code>. Invokes the superclass constructor for <code>setUp()</code>, which is required by JUnit.
This turns off <strong>touch mode</strong> in the device or emulator. If any of your test methods send key events to the application,
you must turn off touch mode <em>before</em> you start any activities; otherwise, the call is ignored.
</li>
<li>
Stores references to system objects. Retrieves and stores a reference to the activity under test, the <code>Spinner</code>
widget used by the activity, the <code>SpinnerAdapter</code> that backs the widget, and the string value of the selection that is
set when the application is first installed. These objects are used in the state management test. The methods invoked are:
<ul>
<li>
{@link android.test.ActivityInstrumentationTestCase2#getActivity()}. Gets a reference to the activity under test (<code>SpinnerActivity</code>).
This call also starts the activity if it is not already running.
</li>
<li>
{@link android.app.Activity#findViewById(int)}. Gets a reference to the <code>Spinner</code> widget of the application under test.
</li>
<li>
{@link android.widget.AbsSpinner#getAdapter()}. Gets a reference to the adapter (an array of strings) backing the spinner.
</li>
</ul>
</li>
</ul>
<p>
Add this code to the definition of <code>SpinnerActivityTest</code>, after the constructor definition:
</p>
<pre>
@Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
mActivity = getActivity();
mSpinner =
(Spinner) mActivity.findViewById(
com.android.example.spinner.R.id.Spinner01
);
mPlanetData = mSpinner.getAdapter();
} // end of setUp() method definition
</pre>
<p>
Add these members to the test case class:
</p>
<pre>
private SpinnerActivity mActivity;
private Spinner mSpinner;
private SpinnerAdapter mPlanetData;
</pre>
<p>
Add these imports:
</p>
<pre>
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
</pre>
<p>
You now have the the complete <code>setUp()</code> method.
</p>
<h3 id="AddPreConditionsTest">Adding an initial conditions test</h3>
<p>
The initial conditions test verifies that the application under test is initialized correctly. It is an illustration of the types of tests you can run, so it is not comprehensive.
It verifies the following:
</p>
<ul>
<li>
The item select listener is initialized. This listener is called when a selection is made from the spinner.
</li>
<li>
The adapter that provides values to the spinner is initialized.
</li>
<li>
The adapter contains the right number of entries.
</li>
</ul>
<p>
The actual initialization of the application under test is done in <code>setUp()</code>, which the test runner calls automatically before every test. The verifications are
done with JUnit {@link junit.framework.Assert} calls. As a useful convention, the method name is <code>testPreConditions()</code>:
Now create a UI test that selects an item from the <code>Spinner</code> widget. The test sends key events to the UI with key events.
The test confirms that the selection matches the result you expect.
</p>
<p>
This test demonstrates the power of using instrumentation in Android testing. Only an instrumentation-based test class allows you to send key events (or touch events)
to the application under test. With instrumentation, you can test your UI without having to take screenshots, record the screen, or do human-controlled testing.
</p>
<p>
To work with the spinner, the test has to request focus for it and then set it to a known position. The test uses {@link android.view.View#requestFocus() requestFocus()} and
{@link android.widget.AbsSpinner#setSelection(int) setSelection()} to do this. Both of these methods interact with a View in the application under test, so you have to call them
in a special way.
</p>
<p>
Code in a test application that interacts with a View of the application under test must run in the main application's thread, also
known as the <em>UI thread</em>. To do this, you use the {@link android.app.Activity#runOnUiThread(java.lang.Runnable) Activity.runOnUiThread()}
method. You pass the code to <code>runOnUiThread()</code>in an anonymous {@link java.lang.Runnable Runnable} object. To set
To send key events to the UI of the application under test, you use the <a href="{@docRoot}reference/android/test/InstrumentationTestCase.html#sendKeys(int...)">sendKeys</a>() method.
This method does not have to run on the UI thread, since Android uses instrumentation to pass the key events to the application under test.
</p>
<p>
The last part of the test compares the selection made by sending the key events to a pre-determined value. This tests that the spinner is working as intended.
</p>
<p>
The following sections show you how to add the code for this test.
</p>
<ol>
<li>
Get focus and set selection. Create a new method <code>public void testSpinnerUI()</code>. Add
code to to request focus for the spinner and set its position to default or initial position, "Earth". This code is run on the UI thread of
the application under test:
<pre>
public void testSpinnerUI() {
mActivity.runOnUiThread(
new Runnable() {
public void run() {
mSpinner.requestFocus();
mSpinner.setSelection(INITIAL_POSITION);
} // end of run() method definition
} // end of anonymous Runnable object instantiation
); // end of invocation of runOnUiThread
</pre>
<p>
Add the following member to the test case class.
</p>
<pre>
public static final int INITIAL_POSITION = 0;
</pre>
</li>
<li>
Make a selection. Send key events to the spinner to select one of the items. To do this, open the spinner by
"clicking" the center keypad button (sending a DPAD_CENTER key event) and then clicking (sending) the down arrow keypad button five times. Finally,
click the center keypad button again to highlight the desired item. Add the following code:
<pre>
this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
for (int i = 1; i <= TEST_POSITION; i++) {
this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
} // end of for loop
this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
</pre>
<p>
Add the following member to the test case class:
</p>
<pre>
public static final int TEST_POSITION = 5;
</pre>
<p>
This sets the final position of the spinner to "Saturn" (the spinner's backing adapter is 0-based).
</p>
</li>
<li>
Check the result. Query the current state of the spinner, and compare its current selection to the expected value.
Call the method {@link android.widget.AdapterView#getSelectedItemPosition() getSelectedItemPosition()} to find out the current selection position, and then
{@link android.widget.AdapterView#getItemAtPosition(int) getItemAtPosition()} to get the object corresponding to that position (casting it to a String). Assert that
this string value matches the expected value of "Saturn":
The first test verifies that the spinner selection is maintained after the entire application is shut down and then restarted. The test uses instrumentation to
set the spinner's variables outside of the UI. It then terminates the activity by calling {@link android.app.Activity#finish() Activity.finish()}, and restarts it
using the instrumentation method {@link android.test.ActivityInstrumentationTestCase2#getActivity()}. The test then asserts that the current spinner state matches
the test values.
</p>
<p>
The second test verifies that the spinner selection is maintained after the activity is paused and then resumed. The test uses instrumentation to
set the spinner's variables outside of the UI and then force calls to the <code>onPause()</code> and <code>onResume()</code> methods. The test then
asserts that the current spinner state matches the test values.
</p>
<p>
Notice that these tests make limited assumptions about the mechanism by which the activity manages state. The tests use the activity's getters and
setters to control the spinner. The first test also knows that hiding an activity calls <code>onPause()</code>, and bringing it back to the foreground
calls <code>onResume()</code>. Other than this, the tests treat the activity as a "black box".
</p>
<p>
To add the code for testing state management across shutdown and restart, follow these steps:
</p>
<ol>
<li>
Add the test method <code>testStateDestroy()</code>, then
The value of <code><APItarget></code> should be "3" (API level 3) or higher. If you are already developing with a particular API level, and it is
higher than 3, then use that API level.
</p>
<p>
This a new Android project <code>SpinnerActivity</code> in the existing <code>Spinner</code> directory. The existing source and
resource files are not touched, but the <code>android</code> tool adds the necessary build files.
</p>
</li>
<li>
Create a new Android test project using <code>android create test-project</code>:
<strong>Note:</strong> Running <code>android create test-project</code> will automatically create
the file <code>AndroidManifest.xml</code> with the correct <code><instrumentation></code> element.
</p>
</li>
<li>
Build the sample application. If you are building with Ant, then it is easiest to use the command <code>ant debug</code> to build a debug version, since the SDK comes
with a debug signing key. The result will be the file <code>Spinner/bin/SpinnerActivity-debug.apk</code>.
You can install this to your device or emulator. Attach your device or start the emulator if you haven't already, and run the command: