| quddusc | 2ef3952 | 2013-06-21 16:49:46 -0700 | [diff] [blame] | 1 | page.title=Testing UI Components |
| 2 | trainingnavtop=true |
| 3 | |
| 4 | @jd:body |
| 5 | |
| 6 | <!-- This is the training bar --> |
| 7 | <div id="tb-wrapper"> |
| 8 | <div id="tb"> |
| 9 | |
| 10 | <h2>This lesson teaches you to</h2> |
| 11 | <ol> |
| 12 | <li><a href="#testcase">Create a Test Case for UI Testing with Instrumentation</a> |
| 13 | <li><a href="#test_method">Add Test Methods to Verify UI Behavior</a> |
| 14 | <ol> |
| 15 | <li><a href="#verify_button_display">Verify Button Layout Parameters</a></li> |
| 16 | <li><a href="#verify_TextView">Verify TextView Layout Parameters</a></li> |
| 17 | <li><a href="#verify_button_behavior">Verify Button Behavior</a></li> |
| 18 | </ol> |
| 19 | </li> |
| 20 | <li><a href="#annotations">Apply Test Annotations</a></li> |
| 21 | </ol> |
| 22 | |
| 23 | <h2>Try it out</h2> |
| 24 | <div class="download-box"> |
| 25 | <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip" |
| 26 | class="button">Download the demo</a> |
| 27 | <p class="filename">AndroidTestingFun.zip</p> |
| 28 | </div> |
| 29 | |
| 30 | </div> |
| 31 | </div> |
| 32 | |
| 33 | <p>Typically, your {@link android.app.Activity} includes user interface |
| 34 | components (such as buttons, editable text fields, checkboxes, and pickers) to |
| 35 | allow users to interact with your Android application. This lesson shows how |
| 36 | you can test an {@link android.app.Activity} with a simple push-button UI. You |
| 37 | can use the same general steps to test other, more sophisticated types of UI |
| 38 | components.</p> |
| 39 | |
| 40 | <p class="note"><strong>Note:</strong> The type of UI testing in this lesson is |
| 41 | called <em>white-box testing</em> because you have the |
| 42 | source code for the application that you want to test. The Android |
| 43 | <a href="{@docRoot}tools/testing/testing_android.html#Instrumentation">Instrumentation</a> |
| 44 | framework is suitable for creating white-box tests for UI components within an |
| 45 | application. An alternative type of UI testing is <em>black-box testing</em>, |
| 46 | where you may not have access to the application source. This type of testing |
| 47 | is useful when you want to test how your app interacts with other apps or with |
| 48 | the system. Black-box testing is not covered in this training. To learn more |
| 49 | about how to perform black-box testing on your Android apps, see the |
| 50 | <a href="{@docRoot}tools/testing/testing_ui.html">UI Testing guide</a>. |
| 51 | <p>For a complete test case example, take a look at |
| 52 | {@code ClickFunActivityTest.java} in the sample app.</p> |
| 53 | |
| 54 | <h2 id="testcase">Create a Test Case for UI Testing with Instrumentation</h2> |
| 55 | <p>When testing an {@link android.app.Activity} that has a user interface (UI), |
| 56 | the {@link android.app.Activity} under test runs in the UI thread. However, the |
| 57 | test application itself runs in a separate thread in the same process as the |
| 58 | application under test. This means that your test app can reference objects |
| 59 | from the UI thread, but if it attempts to change properties on those objects or |
| 60 | send events to the UI thread, you will usually get a {@code WrongThreadException} |
| 61 | error.</p> |
| 62 | <p>To safely inject {@link android.content.Intent} objects into your |
| 63 | {@link android.app.Activity} or run test methods on the UI thread, you can |
| 64 | extend your test class to use {@link android.test.ActivityInstrumentationTestCase2}. |
| 65 | To learn more about how to run test methods on the UI thread, see |
| 66 | <a href="{@docRoot}tools/testing/activity_testing.html#RunOnUIThread">Testing |
| 67 | on the UI thread</a>.</p> |
| 68 | |
| 69 | <h3 id="fixture">Set Up Your Test Fixture</h3> |
| 70 | <p>When setting up the test fixture for UI testing, you should specify the |
| 71 | <a href="{@docRoot}guide/topics/ui/ui-events.html#TouchMode">touch mode</a> |
| 72 | in your {@link junit.framework.TestCase#setUp()} method. Setting the touch mode |
| 73 | to {@code true} prevents the UI control from taking focus when you click it |
| 74 | programmatically in the test method later (for example, a button UI will just |
| 75 | fire its on-click listener). Make sure that you call |
| 76 | {@link android.test.ActivityInstrumentationTestCase2#setActivityInitialTouchMode(boolean) setActivityInitialTouchMode()} |
| 77 | before calling {@link android.test.ActivityInstrumentationTestCase2#getActivity()}. |
| 78 | </p> |
| 79 | <p>For example:</ap> |
| 80 | <pre> |
| 81 | public class ClickFunActivityTest |
| 82 | extends ActivityInstrumentationTestCase2<ClickFunActivity> { |
| 83 | ... |
| 84 | @Override |
| 85 | protected void setUp() throws Exception { |
| 86 | super.setUp(); |
| 87 | |
| 88 | setActivityInitialTouchMode(true); |
| 89 | |
| 90 | mClickFunActivity = getActivity(); |
| 91 | mClickMeButton = (Button) |
| 92 | mClickFunActivity |
| 93 | .findViewById(R.id.launch_next_activity_button); |
| 94 | mInfoTextView = (TextView) |
| 95 | mClickFunActivity.findViewById(R.id.info_text_view); |
| 96 | } |
| 97 | } |
| 98 | </pre> |
| 99 | |
| 100 | <h2 id="test_methods">Add Test Methods to Validate UI Behavior</h2> |
| 101 | <p id="test_goals">Your UI testing goals might include:</p> |
| 102 | <ul> |
| 103 | <li>Verifying that a button is displayed with the correct layout when the |
| 104 | {@link android.app.Activity} is launched.</li> |
| 105 | <li>Verifying that a {@link android.widget.TextView} is initially hidden.</li> |
| 106 | <li>Verifying that a {@link android.widget.TextView} displays the expected string |
| 107 | when a button is pushed.</li> |
| 108 | </ul> |
| 109 | <p>The following section demonstrates how you can implement test methods |
| 110 | to perform these verifications.</p> |
| 111 | |
| 112 | <h3 id="verify_button_display">Verify Button Layout Parameters</h3> |
| 113 | <p>You might add a test method like this to verify that a button is displayed |
| 114 | correctly in your {@link android.app.Activity}:</p> |
| 115 | <pre> |
| 116 | @MediumTest |
| 117 | public void testClickMeButton_layout() { |
| 118 | final View decorView = mClickFunActivity.getWindow().getDecorView(); |
| 119 | |
| 120 | ViewAsserts.assertOnScreen(decorView, mClickMeButton); |
| 121 | |
| 122 | final ViewGroup.LayoutParams layoutParams = |
| 123 | mClickMeButton.getLayoutParams(); |
| 124 | assertNotNull(layoutParams); |
| 125 | assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); |
| 126 | assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); |
| 127 | } |
| 128 | </pre> |
| 129 | |
| 130 | <p>In the {@link android.test.ViewAsserts#assertOnScreen(android.view.View,android.view.View) assertOnScreen()} |
| 131 | method call, you should pass in the root view and the view that you are |
| 132 | expecting to be present on the screen. If the expected view is not found in the |
| 133 | root view, the assertion method throws an {@link junit.framework.AssertionFailedError} |
| 134 | exception, otherwise the test passes.</p> |
| 135 | <p>You can also verify that the layout of a {@link android.widget.Button} is |
| 136 | correct by getting a reference to its {@link android.view.ViewGroup.LayoutParams} |
| 137 | object, then call assertion methods to verify that the |
| 138 | {@link android.widget.Button} object's width and height attributes match the |
| 139 | expected values.</p> |
| 140 | <p>The {@code @MediumTest} annotation specifies how the test is categorized, |
| 141 | relative to its absolute execution time. To learn more about using test size |
| 142 | annotations, see <a href="#annotations">Apply Test Annotations</a>.</p> |
| 143 | |
| 144 | <h3 id="verify_TextView">Verify TextView Layout Parameters</h3> |
| 145 | <p>You might add a test method like this to verify that a |
| 146 | {@link android.widget.TextView} initially appears hidden in |
| 147 | your {@link android.app.Activity}:</p> |
| 148 | <pre> |
| 149 | @MediumTest |
| 150 | public void testInfoTextView_layout() { |
| 151 | final View decorView = mClickFunActivity.getWindow().getDecorView(); |
| 152 | ViewAsserts.assertOnScreen(decorView, mInfoTextView); |
| 153 | assertTrue(View.GONE == mInfoTextView.getVisibility()); |
| 154 | } |
| 155 | </pre> |
| 156 | <p>You can call {@link android.view.Window#getDecorView()} to get a reference |
| 157 | to the decor view for the {@link android.app.Activity}. The decor view is the |
| 158 | top-level ViewGroup ({@link android.widget.FrameLayout}) view in the layout |
| 159 | hierarchy.</p> |
| 160 | |
| 161 | <h3 id="verify_button_behavior">Verify Button Behavior</h3> |
| 162 | <p>You can use a test method like this to verify that a |
| 163 | {@link android.widget.TextView} becomes visible when a |
| 164 | {@link android.widget.Button} is pushed:</p> |
| 165 | |
| 166 | <pre> |
| 167 | @MediumTest |
| 168 | public void testClickMeButton_clickButtonAndExpectInfoText() { |
| 169 | String expectedInfoText = mClickFunActivity.getString(R.string.info_text); |
| 170 | TouchUtils.clickView(this, mClickMeButton); |
| 171 | assertTrue(View.VISIBLE == mInfoTextView.getVisibility()); |
| 172 | assertEquals(expectedInfoText, mInfoTextView.getText()); |
| 173 | } |
| 174 | </pre> |
| 175 | |
| 176 | <p>To programmatically click a {@link android.widget.Button} in your |
| 177 | test, call {@link android.test.TouchUtils#clickView(android.test.InstrumentationTestCase,android.view.View) clickView()}. |
| 178 | You must pass in a reference to the test case that is being run and a reference |
| 179 | to the {@link android.widget.Button} to manipulate.</p> |
| 180 | |
| 181 | <p class="note"><strong>Note: </strong>The {@link android.test.TouchUtils} |
| 182 | helper class provides convenience methods for simulating touch interactions |
| 183 | with your application. You can use these methods to simulate clicking, tapping, |
| 184 | and dragging of Views or the application screen.</p> |
| 185 | <p class="caution"><strong>Caution: </strong>The {@link android.test.TouchUtils} |
| 186 | methods are designed to send events to the UI thread safely from the test thread. |
| 187 | You should not run {@link android.test.TouchUtils} directly in the UI thread or |
| 188 | any test method annotated with {@code @UIThread}. Doing so might |
| 189 | raise the {@code WrongThreadException}.</p> |
| 190 | |
| 191 | <h2 id="annotations">Apply Test Annotations</h2> |
| 192 | <p>The following annotations can be applied to indicate the size of a test |
| 193 | method:</p> |
| 194 | <dl> |
| 195 | <dt>{@link |
| 196 | android.test.suitebuilder.annotation.SmallTest @SmallTest}</dt> |
| 197 | <dd>Marks a test that should run as part of the small tests.</dd> |
| 198 | <dt>{@link |
| 199 | android.test.suitebuilder.annotation.MediumTest @MediumTest}</dt> |
| 200 | <dd>Marks a test that should run as part of the medium tests.</dd> |
| 201 | <dt>{@link android.test.suitebuilder.annotation.LargeTest @LargeTest}</dt> |
| 202 | <dd>Marks a test that should run as part of the large tests.</dd> |
| 203 | </dl> |
| 204 | <p>Typically, a short running test that take only a few milliseconds should be |
| 205 | marked as a {@code @SmallTest}. Longer running tests (100 milliseconds or |
| 206 | more) are usually marked as {@code @MediumTest}s or {@code @LargeTest}s, |
| 207 | depending on whether the test accesses resources on the local system only or |
| 208 | remote resources over a network. For guidance on using test size annotations, |
| 209 | see this <a href="https://plus.sandbox.google.com/+AndroidDevelopers/posts/TPy1EeSaSg8">Android Tools Protip</a>.</p> |
| 210 | <p>You can mark up your test methods with other test annotations to control |
| 211 | how the tests are organized and run. For more information on other annotations, |
| 212 | see the {@link java.lang.annotation.Annotation} class reference.</p> |
| 213 | |
| 214 | |
| 215 | |
| 216 | |