page.title=Testing UI Components 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="#testcase">Create a Test Case for UI Testing with Instrumentation</a> <li><a href="#test_method">Add Test Methods to Verify UI Behavior</a> <ol> <li><a href="#verify_button_display">Verify Button Layout Parameters</a></li> <li><a href="#verify_TextView">Verify TextView Layout Parameters</a></li> <li><a href="#verify_button_behavior">Verify Button Behavior</a></li> </ol> </li> <li><a href="#annotations">Apply Test Annotations</a></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>Typically, your {@link android.app.Activity} includes user interface components (such as buttons, editable text fields, checkboxes, and pickers) to allow users to interact with your Android application. This lesson shows how you can test an {@link android.app.Activity} with a simple push-button UI. You can use the same general steps to test other, more sophisticated types of UI components.</p> <p class="note"><strong>Note:</strong> The type of UI testing in this lesson is called <em>white-box testing</em> because you have the source code for the application that you want to test. The Android <a href="{@docRoot}tools/testing/testing_android.html#Instrumentation">Instrumentation</a> framework is suitable for creating white-box tests for UI components within an application. An alternative type of UI testing is <em>black-box testing</em>, where you may not have access to the application source. This type of testing is useful when you want to test how your app interacts with other apps or with the system. Black-box testing is not covered in this training. To learn more about how to perform black-box testing on your Android apps, see the <a href="{@docRoot}tools/testing/testing_ui.html">UI Testing guide</a>. <p>For a complete test case example, take a look at {@code ClickFunActivityTest.java} in the sample app.</p> <h2 id="testcase">Create a Test Case for UI Testing with Instrumentation</h2> <p>When testing an {@link android.app.Activity} that has a user interface (UI), the {@link android.app.Activity} under test runs in the UI thread. However, the test application itself runs in a separate thread in the same process as the application under test. This means that your test app can reference objects from the UI thread, but if it attempts to change properties on those objects or send events to the UI thread, you will usually get a {@code WrongThreadException} error.</p> <p>To safely inject {@link android.content.Intent} objects into your {@link android.app.Activity} or run test methods on the UI thread, you can extend your test class to use {@link android.test.ActivityInstrumentationTestCase2}. To learn more about how to run test methods on the UI thread, see <a href="{@docRoot}tools/testing/activity_testing.html#RunOnUIThread">Testing on the UI thread</a>.</p> <h3 id="fixture">Set Up Your Test Fixture</h3> <p>When setting up the test fixture for UI testing, you should specify the <a href="{@docRoot}guide/topics/ui/ui-events.html#TouchMode">touch mode</a> in your {@link junit.framework.TestCase#setUp()} method. Setting the touch mode to {@code true} prevents the UI control from taking focus when you click it programmatically in the test method later (for example, a button UI will just fire its on-click listener). Make sure that you call {@link android.test.ActivityInstrumentationTestCase2#setActivityInitialTouchMode(boolean) setActivityInitialTouchMode()} before calling {@link android.test.ActivityInstrumentationTestCase2#getActivity()}. </p> <p>For example:</ap> <pre> public class ClickFunActivityTest extends ActivityInstrumentationTestCase2<ClickFunActivity> { ... @Override protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mClickFunActivity = getActivity(); mClickMeButton = (Button) mClickFunActivity .findViewById(R.id.launch_next_activity_button); mInfoTextView = (TextView) mClickFunActivity.findViewById(R.id.info_text_view); } } </pre> <h2 id="test_methods">Add Test Methods to Validate UI Behavior</h2> <p id="test_goals">Your UI testing goals might include:</p> <ul> <li>Verifying that a button is displayed with the correct layout when the {@link android.app.Activity} is launched.</li> <li>Verifying that a {@link android.widget.TextView} is initially hidden.</li> <li>Verifying that a {@link android.widget.TextView} displays the expected string when a button is pushed.</li> </ul> <p>The following section demonstrates how you can implement test methods to perform these verifications.</p> <h3 id="verify_button_display">Verify Button Layout Parameters</h3> <p>You might add a test method like this to verify that a button is displayed correctly in your {@link android.app.Activity}:</p> <pre> @MediumTest public void testClickMeButton_layout() { final View decorView = mClickFunActivity.getWindow().getDecorView(); ViewAsserts.assertOnScreen(decorView, mClickMeButton); final ViewGroup.LayoutParams layoutParams = mClickMeButton.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } </pre> <p>In the {@link android.test.ViewAsserts#assertOnScreen(android.view.View,android.view.View) assertOnScreen()} method call, you should pass in the root view and the view that you are expecting to be present on the screen. If the expected view is not found in the root view, the assertion method throws an {@link junit.framework.AssertionFailedError} exception, otherwise the test passes.</p> <p>You can also verify that the layout of a {@link android.widget.Button} is correct by getting a reference to its {@link android.view.ViewGroup.LayoutParams} object, then call assertion methods to verify that the {@link android.widget.Button} object's width and height attributes match the expected values.</p> <p>The {@code @MediumTest} annotation specifies how the test is categorized, relative to its absolute execution time. To learn more about using test size annotations, see <a href="#annotations">Apply Test Annotations</a>.</p> <h3 id="verify_TextView">Verify TextView Layout Parameters</h3> <p>You might add a test method like this to verify that a {@link android.widget.TextView} initially appears hidden in your {@link android.app.Activity}:</p> <pre> @MediumTest public void testInfoTextView_layout() { final View decorView = mClickFunActivity.getWindow().getDecorView(); ViewAsserts.assertOnScreen(decorView, mInfoTextView); assertTrue(View.GONE == mInfoTextView.getVisibility()); } </pre> <p>You can call {@link android.view.Window#getDecorView()} to get a reference to the decor view for the {@link android.app.Activity}. The decor view is the top-level ViewGroup ({@link android.widget.FrameLayout}) view in the layout hierarchy.</p> <h3 id="verify_button_behavior">Verify Button Behavior</h3> <p>You can use a test method like this to verify that a {@link android.widget.TextView} becomes visible when a {@link android.widget.Button} is pushed:</p> <pre> @MediumTest public void testClickMeButton_clickButtonAndExpectInfoText() { String expectedInfoText = mClickFunActivity.getString(R.string.info_text); TouchUtils.clickView(this, mClickMeButton); assertTrue(View.VISIBLE == mInfoTextView.getVisibility()); assertEquals(expectedInfoText, mInfoTextView.getText()); } </pre> <p>To programmatically click a {@link android.widget.Button} in your test, call {@link android.test.TouchUtils#clickView(android.test.InstrumentationTestCase,android.view.View) clickView()}. You must pass in a reference to the test case that is being run and a reference to the {@link android.widget.Button} to manipulate.</p> <p class="note"><strong>Note: </strong>The {@link android.test.TouchUtils} helper class provides convenience methods for simulating touch interactions with your application. You can use these methods to simulate clicking, tapping, and dragging of Views or the application screen.</p> <p class="caution"><strong>Caution: </strong>The {@link android.test.TouchUtils} methods are designed to send events to the UI thread safely from the test thread. You should not run {@link android.test.TouchUtils} directly in the UI thread or any test method annotated with {@code @UIThread}. Doing so might raise the {@code WrongThreadException}.</p> <h2 id="annotations">Apply Test Annotations</h2> <p>The following annotations can be applied to indicate the size of a test method:</p> <dl> <dt>{@link android.test.suitebuilder.annotation.SmallTest @SmallTest}</dt> <dd>Marks a test that should run as part of the small tests.</dd> <dt>{@link android.test.suitebuilder.annotation.MediumTest @MediumTest}</dt> <dd>Marks a test that should run as part of the medium tests.</dd> <dt>{@link android.test.suitebuilder.annotation.LargeTest @LargeTest}</dt> <dd>Marks a test that should run as part of the large tests.</dd> </dl> <p>Typically, a short running test that take only a few milliseconds should be marked as a {@code @SmallTest}. Longer running tests (100 milliseconds or more) are usually marked as {@code @MediumTest}s or {@code @LargeTest}s, depending on whether the test accesses resources on the local system only or remote resources over a network. For guidance on using test size annotations, see this <a href="https://plus.sandbox.google.com/+AndroidDevelopers/posts/TPy1EeSaSg8">Android Tools Protip</a>.</p> <p>You can mark up your test methods with other test annotations to control how the tests are organized and run. For more information on other annotations, see the {@link java.lang.annotation.Annotation} class reference.</p>