am 0c3b894c: am 91070dc4: Merge "docs: Added training docs for UI testing frameworks (Espresso, UI Automator)." into lmp-docs

* commit '0c3b894c2c18b226f58af98569f21bcb4a593b14':
  docs: Added training docs for UI testing frameworks (Espresso, UI Automator).
This commit is contained in:
Quddus Chong
2015-04-03 21:08:49 +00:00
committed by Android Git Automerger
5 changed files with 1199 additions and 2 deletions

View File

@@ -391,7 +391,9 @@ onView(withId(R.id.changeTextBt)).perform(click());</pre>
<p>
To learn more about using Espresso, see the
<a href="{@docRoot}reference/android/support/test/package-summary.html">API reference</a>.
<a href="{@docRoot}reference/android/support/test/package-summary.html">API reference</a> and
<a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">
Testing UI for a Single App</a> training.
</p>
<h3 id="UIAutomator">
@@ -531,7 +533,9 @@ allAppsButton.clickAndWaitForNewWindow();</pre>
<p>
To learn more about using UI Automator, see the
<a href="{@docRoot}reference/android/support/test/package-summary.html">API reference</a>.
<a href="{@docRoot}reference/android/support/test/package-summary.html">API reference</a> and
<a href="{@docRoot}training/testing/ui-testing/uiautomator-testing.html">
Testing UI for Multiple Apps</a> training.
</p>
<h2 id="setup">

View File

@@ -0,0 +1,579 @@
page.title=Testing UI for a Single App
page.tags=testing,espresso
trainingnavtop=true
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and Prerequisites</h2>
<ul>
<li>Android 2.2 (API level 8) or higher
</li>
<li>
<a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support
Library</a>
</li>
</ul>
<h2>
This lesson teaches you to
</h2>
<ol>
<li>
<a href="#setup">Set Up Espresso</a>
</li>
<li>
<a href="#build">Create an Espresso Test Class</a>
</li>
<li>
<a href="#run">Run Espresso Tests on a Device or Emulator</a>
</li>
</ol>
<h2>
You should also read
</h2>
<ul>
<li><a href="{@docRoot}reference/android/support/test/package-summary.html">
Espresso API Reference</a></li>
</ul>
<h2>
Try it out
</h2>
<ul>
<li>
<a href="https://github.com/googlesamples/android-testing"
class="external-link">Espresso Code Samples</a>
</li>
</ul>
</div>
</div>
<p>
UI tests that involve user interactions
within a single app help to ensure that users do not
encounter unexpected results or have a poor experience when interacting with your app.
You should get into the habit of creating user interface (UI) tests if you need to verify
that the UI of your app is functioning correctly.
</p>
<p>
The Espresso testing framework, provided by the
<a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>,
provides APIs for writing UI tests to simulate user interactions within a
single target app. Espresso tests can run on devices running Android 2.2 (API level 8) and
higher. A key benefit of using Espresso is that it provides automatic synchronization of test
actions with the UI of the app you are testing. Espresso detects when the main thread is idle,
so it is able to run your test commands at the appropriate time, improving the reliability of
your tests. This capability also relieves you from having to adding any timing workarounds,
such as a sleep period, in your test code.
</p>
<p>
The Espresso testing framework is an instrumentation-based API and works
with the
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code
AndroidJUnitRunner}</a> test runner.
</p>
<h2 id="setup">
Set Up Espresso
</h2>
<p>
Before you begin using Espresso, you must:
</p>
<ul>
<li>
<strong>Install the Android Testing Support Library</strong>. The Espresso API is
located under the {@code com.android.support.test.espresso} package. These classes allow
you to create tests that use the Espresso testing framework. To learn how to install the
library, see <a href="{@docRoot}tools/testing-support-library/index.html#setup">
Testing Support Library Setup</a>.
</li>
<li>
<strong>Set up your project structure.</strong> In your Gradle project, the source code for
the target app that you want to test is typically placed under the {@code app/src/main}
folder. The source code for instrumentation tests, including
your Espresso tests, must be placed under the <code>app/src/androidTest</code> folder. To
learn more about setting up your project directory, see
<a href="{@docRoot}tools/projects/index.html">Managing Projects</a>.
</li>
<li>
<strong>Specify your Android testing dependencies</strong>. In order for the
<a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for Gradle</a> to
correctly build and run your Espresso tests, you must specify the following libraries in
the {@code build.gradle} file of your Android app module:
<pre>
dependencies {
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
}
</pre>
</li>
<li>
<strong>Turn off animations on your test device.</strong> Leaving system animations turned
on in the test device might cause unexpected results or may lead your test to fail. Turn
off animations from <em>Settings</em> by opening <em>Developing Options</em> and
turning all the following options off:
<ul>
<li>
<em>Window animation scale</em>
</li>
<li>
<em>Transition animation scale</em>
</li>
<li>
<em>Animator duration scale</em>
</li>
</ul>
</li>
</ul>
<h2 id="build">
Create an Espresso Test Class
</h2>
<p>
To create an Espresso test, create a Java class or an
{@link android.test.ActivityInstrumentationTestCase2}
subclass that follows this programming model:
</p>
<ol>
<li>Find the UI component you want to test in an {@link android.app.Activity} (for example, a
sign-in button in the app) by calling the
<a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onView(org.hamcrest.Matcher<android.view.View>)">
{@code onView()}</a> method, or the
<a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onData(org.hamcrest.Matcher<java.lang.Object>)">
{@code onData()}</a> method for {@link android.widget.AdapterView} controls.
</li>
<li>Simulate a specific user interaction to perform on that UI component, by calling the
<a href="{@docRoot}reference/android/support/test/espresso/ViewInteraction.html#perform(android.support.test.espresso.ViewAction...)">{@code ViewInteraction.perform()}</a>
or
<a href="{@docRoot}reference/android/support/test/espresso/DataInteraction.html#perform(android.support.test.espresso.ViewAction...)">{@code DataInteraction.perform()}</a>
method and passing in the user action (for example, click on the sign-in button). To sequence
multiple actions on the same UI component, chain them using a comma-separated list in your
method argument.
</li>
<li>Repeat the steps above as necessary, to simulate a user flow across multiple
activities in the target app.
</li>
<li>Use the
<a href="{@docRoot}reference/android/support/test/espresso/assertion/ViewAssertions.html">{@code ViewAssertions}</a>
methods to check that the UI reflects the expected
state or behavior, after these user interactions are performed.
</li>
</ol>
<p>
These steps are covered in more detail in the sections below.
</p>
<p>
The following code snippet shows how your test class might invoke this basic workflow:
</p>
<pre>
onView(withId(R.id.my_view)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()) // click() is a ViewAction
.check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion
</pre>
<h3 id="espresso-aitc2">
Using Espresso with ActivityInstrumentationTestCase2
</h3>
<p>
If you are subclassing {@link android.test.ActivityInstrumentationTestCase2}
to create your Espresso test class, you must inject an
{@link android.app.Instrumentation} instance into your test class. This step is required in
order for your Espresso test to run with the
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code AndroidJUnitRunner}</a>
test runner.
</p>
<p>
To do this, call the
{@link android.test.InstrumentationTestCase#injectInstrumentation(android.app.Instrumentation) injectInstrumentation()}
method and pass in the result of
<a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html#getInstrumentation()">
{@code InstrumentationRegistry.getInstrumentation()}</a>, as shown in the following code
example:
</p>
<pre>
import android.support.test.InstrumentationRegistry;
public class MyEspressoTest
extends ActivityInstrumentationTestCase2&lt;MyActivity&gt; {
private MyActivity mActivity;
public MyEspressoTest() {
super(MyActivity.class);
}
&#64;Before
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
mActivity = getActivity();
}
...
}
</pre>
<p class="note"><strong>Note:</strong> Previously, {@link android.test.InstrumentationTestRunner}
would inject the {@link android.app.Instrumentation} instance, but this test runner is being
deprecated.</p>
<h3 id="accessing-ui-components">
Accessing UI Components
</h3>
<p>
Before Espresso can interact with the app under test, you must first specify the UI component
or <em>view</em>. Espresso supports the use of
<a href="http://hamcrest.org/" class="external-link">Hamcrest matchers</a>
for specifying views and adapters in your app.
</p>
<p>
To find the view, call the <a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onView(org.hamcrest.Matcher<android.view.View>)">
{@code onView()}</a>
method and pass in a view matcher that specifies the view that you are targeting. This is
described in more detail in <a href="#specifying-view-matcher">Specifying a View Matcher</a>.
The <a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onView(org.hamcrest.Matcher<android.view.View>)">
{@code onView()}</a> method returns a
<a href="{@docRoot}reference/android/support/test/espresso/ViewInteraction.html">
{@code ViewInteraction}</a>
object that allows your test to interact with the view.
However, calling the <a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onView(org.hamcrest.Matcher<android.view.View>)">
{@code onView()}</a> method may not work if you want to locate a view in
an {@link android.widget.AdapterView} layout. In this case, follow the instructions in
<a href="#locating-adpeterview-view">Locating a view in an AdapterView</a> instead.
</p>
<p class="note">
<strong>Note</strong>: The <a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onView(org.hamcrest.Matcher<android.view.View>)">
{@code onView()}</a> method does not check if the view you specified is
valid. Instead, Espresso searches only the current view hierarchy, using the matcher provided.
If no match is found, the method throws a
<a href="{@docRoot}reference/android/support/test/espresso/NoMatchingViewException.html">
{@code NoMatchingViewException}</a>.
</p>
<p>
The following code snippet shows how you might write a test that accesses an
{@link android.widget.EditText} field, enters a string of text, closes the virtual keyboard,
and then performs a button click.
</p>
<pre>
public void testChangeText_sameActivity() {
// Type text and then press the button.
onView(withId(R.id.editTextUserInput))
.perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());
onView(withId(R.id.changeTextButton)).perform(click());
// Check that the text was changed.
...
}
</pre>
<h4 id="specifying-view-matcher">
Specifying a View Matcher
</h4>
<p>
You can specify a view matcher by using these approaches:
</p>
<ul>
<li>Calling methods in the
<a href="{@docRoot}reference/android/support/test/espresso/matcher/ViewMatchers.html">
{@code ViewMatchers}</a> class. For example, to find a view by looking for a text string it
displays, you can call a method like this:
<pre>
onView(withText("Sign-in"));
</pre>
<p>Similarly you can call
<a href="{@docRoot}reference/android/support/test/espresso/matcher/ViewMatchers.html#withId(int)">
{@code withId()}</a> and providing the resource ID ({@code R.id}) of the view, as shown in the
following example:</p>
<pre>
onView(withId(R.id.button_signin));
</pre>
<p>
Android resource IDs are not guaranteed to be unique. If your test attempts to match to a
resource ID used by more than one view, Espresso throws an
<a href="{@docRoot}reference/android/support/test/espresso/AmbiguousViewMatcherException.html">
{@code AmbiguousViewMatcherException}</a>.
</p>
</li>
<li>Using the Hamcrest
<a href="http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html"
class="external-link">{@code Matchers}</a> class. You can use the
{@code allOf()} methods to combine multiple matchers, such as
{@code containsString()} and {@code instanceOf()}. This approach allows you to
filter the match results more narrowly, as shown in the following example:
<pre>
onView(allOf(withId(R.id.button_signin), withText("Sign-in")));
</pre>
<p>You can use the {@code not} keyword to filter for views that don't correspond to the matcher, as
shown in the following example:</p>
<pre>
onView(allOf(withId(R.id.button_signin), not(withText("Sign-out"))));
</pre>
<p>To use these methods in your test, import the {@code org.hamcrest.Matchers} package. To
learn more about Hamcrest matching, see the
<a href="http://hamcrest.org/" class="external-link">Hamcrest site</a>.
</p>
</li>
</ul>
<p>
To improve the performance of your Espresso tests, specify the minimum matching information
needed to find your target view. For example, if a view is uniquely identifiable by its
descriptive text, you do not need to specify that it is also assignable from the
{@link android.widget.TextView} instance.
</p>
<h4 id="#locating-adpeterview-view">
Locating a view in an AdapterView
</h4>
<p>
In an {@link android.widget.AdapterView} widget, the view is dynamically populated with child
views at runtime. If the target view you want to test is inside an
{@link android.widget.AdapterView}
(such as a {@link android.widget.ListView}, {@link android.widget.GridView}, or
{@link android.widget.Spinner}), the
<a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onView(org.hamcrest.Matcher<android.view.View>)">
{@code onView()}</a> method might not work because only a
subset of the views may be loaded in the current view hierarchy.
</p>
<p>
Instead, call the <a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onData(org.hamcrest.Matcher<java.lang.Object>)">{@code onData()}</a>
method to obtain a
<a href="{@docRoot}reference/android/support/test/espresso/DataInteraction.html">
{@code DataInteraction}</a>
object to access the target view element. Espresso handles loading the target view element
into the current view hierarchy. Espresso also takes care of scrolling to the target element,
and putting the element into focus.
</p>
<p class="note">
<strong>Note</strong>: The
<a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onData(org.hamcrest.Matcher<java.lang.Object>)">{@code onData()}</a>
method does not check if if the item you specified corresponds with a view. Espresso searches
only the current view hierarchy. If no match is found, the method throws a
<a href="{@docRoot}reference/android/support/test/espresso/NoMatchingViewException.html">
{@code NoMatchingViewException}</a>.
</p>
<p>
The following code snippet shows how you can use the
<a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onData(org.hamcrest.Matcher<java.lang.Object>)">{@code onData()}</a>
method together
with Hamcrest matching to search for a specific row in a list that contains a given string.
In this example, the {@code LongListActivity} class contains a list of strings exposed
through a {@link android.widget.SimpleAdapter}.
</p>
<pre>
onData(allOf(is(instanceOf(Map.class)),
hasEntry(equalTo(LongListActivity.ROW_TEXT), is(str))));
</pre>
<h3 id="perform-actions">
Performing Actions
</h3>
<p>
Call the <a href="{@docRoot}reference/android/support/test/espresso/ViewInteraction.html#perform(android.support.test.espresso.ViewAction...)">{@code ViewInteraction.perform()}</a>
or
<a href="{@docRoot}reference/android/support/test/espresso/DataInteraction.html#perform(android.support.test.espresso.ViewAction...)">{@code DataInteraction.perform()}</a>
methods to
simulate user interactions on the UI component. You must pass in one or more
<a href="{@docRoot}reference/android/support/test/espresso/ViewAction.html">{@code ViewAction}</a>
objects as arguments. Espresso fires each action in sequence according to
the given order, and executes them in the main thread.
</p>
<p>
The
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html">{@code ViewActions}</a>
class provides a list of helper methods for specifying common actions.
You can use these methods as convenient shortcuts instead of creating and configuring
individual <a href="{@docRoot}reference/android/support/test/espresso/ViewAction.html">{@code ViewAction}</a>
objects. You can specify such actions as:
</p>
<ul>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#click()">{@code ViewActions.click()}</a>:
Clicks on the view.
</li>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#typeText(java.lang.String)">{@code ViewActions.typeText()}</a>:
Clicks on a view and enters a specified string.
</li>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#scrollTo()">{@code ViewActions.scrollTo()}</a>:
Scrolls to the view. The
target view must be subclassed from {@link android.widget.ScrollView}
and the value of its
<a href="http://developer.android.com/reference/android/view/View.html#attr_android:visibility">{@code android:visibility}</a>
property must be {@link android.view.View#VISIBLE}. For views that extend
{@link android.widget.AdapterView} (for example,
{@link android.widget.ListView}),
the
<a href="{@docRoot}reference/android/support/test/espresso/Espresso.html#onData(org.hamcrest.Matcher<java.lang.Object>)">{@code onData()}</a>
method takes care of scrolling for you.
</li>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#pressKey(int)">{@code ViewActions.pressKey()}</a>:
Performs a key press using a specified keycode.
</li>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#clearText()">{@code ViewActions.clearText()}</a>:
Clears the text in the target view.
</li>
</ul>
<p>
If the target view is inside a {@link android.widget.ScrollView}, perform the
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#scrollTo()">{@code ViewActions.scrollTo()}</a>
action first to display the view in the screen before other proceeding
with other actions. The
<a href="{@docRoot}reference/android/support/test/espresso/action/ViewActions.html#scrollTo()">{@code ViewActions.scrollTo()}</a>
action will have no effect if the view is already displayed.
</p>
<h3 id="verify-results">
Verifying Results
</h3>
<p>
Call the
<a href="{@docRoot}reference/android/support/test/espresso/ViewInteraction.html#check(android.support.test.espresso.ViewAssertion)">{@code ViewInteraction.check()}</a>
or
<a href="{@docRoot}reference/android/support/test/espresso/DataInteraction.html#check(android.support.test.espresso.ViewAssertion)">{@code DataInteraction.check()}</a>
method to assert
that the view in the UI matches some expected state. You must pass in a
<a href="{@docRoot}reference/android/support/test/espresso/ViewAssertion.html">
{@code ViewAssertion}</a> object as the argument. If the assertion fails, Espresso throws
an {@link junit.framework.AssertionFailedError}.
</p>
<p>
The
<a href="{@docRoot}reference/android/support/test/espresso/assertion/ViewAssertions.html">{@code ViewAssertions}</a>
class provides a list of helper methods for specifying common
assertions. The assertions you can use include:
</p>
<ul>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/assertion/ViewAssertions.html#doesNotExist()">{@code doesNotExist}</a>:
Asserts that there is no view matching the specified criteria in the current view hierarchy.
</li>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/assertion/ViewAssertions.html#matches(org.hamcrest.Matcher&lt;? super android.view.View&gt;)">{@code matches}</a>:
Asserts that the specified view exists in the current view hierarchy
and its state matches some given Hamcrest matcher.
</li>
<li>
<a href="{@docRoot}reference/android/support/test/espresso/assertion/ViewAssertions.html#selectedDescendantsMatch(org.hamcrest.Matcher&lt;android.view.View&gt;, org.hamcrest.Matcher&lt;android.view.View&gt;)">{@code selectedDescendentsMatch}</a>
: Asserts that the specified children views for a
parent view exist, and their state matches some given Hamcrest matcher.
</li>
</ul>
<p>
The following code snippet shows how you might check that the text displayed in the UI has
the same value as the text previously entered in the
{@link android.widget.EditText} field.
</p>
<pre>
public void testChangeText_sameActivity() {
// Type text and then press the button.
...
// Check that the text was changed.
onView(withId(R.id.textToBeChanged))
.check(matches(withText(STRING_TO_BE_TYPED)));
}
</pre>
<h2 id="run">Run Espresso Tests on a Device or Emulator</h2>
<p>
To run Espresso tests, you must use the
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code AndroidJUnitRunner}</a>
class provided in the
<a href="{@docRoot}tools/testing-support-library/index.html">
Android Testing Support Library</a> as your default test runner. The
<a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for
Gradle</a> provides a default directory ({@code src/androidTest/java}) for you to store the
instrumented test classes and test suites that you want to run on a device. The
plug-in compiles the test code in that directory and then executes the test app using
the configured test runner class.
</p>
<p>
To run Espresso tests in your Gradle project:
</p>
<ol>
<li>Specify
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code AndroidJUnitRunner}</a>
as the default test instrumentation runner in
your {@code build.gradle} file:
<pre>
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}</pre>
</li>
<li>Run your tests from the command-line by calling the the {@code connectedCheck}
(or {@code cC}) task:
<pre>
./gradlew cC</pre>
</li>
</ol>

View File

@@ -0,0 +1,76 @@
page.title=Automating User Interface Tests
page.tags=testing
trainingnavtop=true
startpage=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>
You should also read
</h2>
<ul>
<li>
<a href="{@docRoot}/tools/testing-support-library/index.html">Testing Support Library</a>
</li>
</ul>
</div>
</div>
<p>User interface (UI) testing lets you ensure that your app meets its functional requirements
and achieves a high standard of quality such that it is more likely to be successfully adopted by
users.</p>
<p>One approach to UI testing is to simply have a human tester perform a set of user operations on
the target app and verify that it is behaving correctly. However, this manual approach can be
time-consuming, tedious, and error-prone. A more efficient approach is to write your UI
tests such that user actions are performed in an automated way. The automated approach allows
you to run your tests quickly and reliably in a repeatable manner.</p>
<p class="note"><strong>Note: </strong>It is strongly encouraged that you use
<a href="{@docRoot}sdk/installing/studio.html">Android Studio</a> for
building your test apps, because it provides project setup, library inclusion, and packaging
conveniences. This class assumes you are using Android Studio.</p>
<p>To automate UI tests with Android Studio, you implement your test code in a separate
Android test folder ({@code src/androidTest/java}). The
<a href="{@docRoot}tools/building/plugin-for-gradle.html">Android
Plug-in for Gradle</a> builds a test app based on your test code, then loads the test app on the
same device as the target app. In your test code, you can use UI testing frameworks to
simulate user interactions on the target app, in order to perform testing tasks that cover specific
usage scenarios.</p>
<p>For testing Android apps, you typically create these types of automated UI tests:</p>
<ul>
<li><em>UI tests that span a single app:</em> This type of test verifies that the target app behaves
as expected when a user performs a specific action or enters a specific input in its activities.
It allows you to check that the target app returns the correct UI output in response
to user interactions in the apps activities. UI testing frameworks like Espresso allow you to
programmatically simulate user actions and test complex intra-app user interactions.</li>
<li><em>UI tests that span multiple apps:</em> This type of test verifies the correct behavior of
interactions between different user apps or between user apps and system apps. For example, you
might want to test that your camera app shares images correctly with a 3rd-party social media app,
or with the default Android Photos app. UI testing frameworks that support cross-app interactions,
such as UI Automator, allow you to create tests for such scenarios.</li>
</ul>
<p>The lessons in this class teach you how to use the tools and APIs in the
<a href="{@docRoot}/tools/testing-support-library/index.html">Android Testing Support Library</a>
to build these types of automated tests. Before you begin building tests using these
APIs, you must install the Android Testing Support Library, as described in
<a href="{@docRoot}/tools/testing-support-library/index.html#setup">Downloading the Android
Testing Support Library</a>.</p>
<h2>Lessons</h2>
<dl>
<dt><strong><a href="espresso-testing.html">
Testing UI for a Single App</a></strong></dt>
<dd>Learn how to test UI in a single app by using the Espresso testing framework.</dd>
<dt><strong><a href="uiautomator-testing.html">
Testing UI for Multiple Apps</a></strong></dt>
<dd>Learn how to test UI in multiple apps by using the UI Automator testing framework</dd>
</dl>

View File

@@ -0,0 +1,520 @@
page.title=Testing UI for Multiple Apps
page.tags=testing,ui automator
trainingnavtop=true
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and Prerequisites</h2>
<ul>
<li>Android 4.3 (API level 18) or higher</li>
<li><a href="{@docRoot}tools/testing-support-library/index.html">
Android Testing Support Library</a></li>
</ul>
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#setup">Set Up UI Automator</a></li>
<li><a href="#build">Create a UI Automator Test Class</a></li>
<li><a href="#run">Run UI Automator Tests on a Device or Emulator</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}reference/android/support/test/package-summary.html">
UI Automator API Reference</a></li>
</ul>
<h2>Try it out</h2>
<ul>
<li><a href="https://github.com/googlesamples/android-testing"
class="external-link">UI Automator Code Samples</a></li>
</ul>
</div>
</div>
<p>A user interface (UI) test that involves user interactions across multiple apps lets you
verify that your app behaves correctly when the user flow crosses into other apps or into the
system UI. An example of such a user flow is a messaging app that lets the user enter a text
message, launches the Android contact picker so that the users can select recipients to send the
message to, and then returns control to the original app for the user to submit the message.</p>
<p>This lesson covers how to write such UI tests using the
UI Automator testing framework provided by the
<a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
The UI Automator APIs let you interact with visible elements on a device, regardless of
which {@link android.app.Activity} is in focus. Your test can look up a UI component by using
convenient descriptors such as the text displayed in that component or its content description. UI
Automator tests can run on devices running Android 4.3 (API level 18) or higher.</p>
<p>The UI Automator testing framework is an instrumentation-based API and works
with the
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
{@code AndroidJUnitRunner}</a>
test runner.
</p>
<h2 id="setup">Set Up UI Automator</h2>
<p>Before you begin using UI Automator, you must:</p>
<ul>
<li>
<strong>Install the Android Testing Support Library</strong>. The UI Automator API is
located under the {@code com.android.support.test.uiautomator} package. These classes allow
you to create tests that use the Espresso testing framework. To learn how to install the
library, see <a href="{@docRoot}tools/testing-support-library/index.html#setup">
Testing Support Library Setup</a>.
</li>
<li>
<strong>Set up your project structure.</strong> In your Gradle project, the source code for
the target app that you want to test is typically placed under the {@code app/src/main}
folder. The source code for instrumentation tests, including
your UI Automator tests, must be placed under the <code>app/src/androidTest</code> folder.
To learn more about setting up your project directory, see
<a href="{@docRoot}tools/projects/index.html">Managing Projects</a>.
</li>
<li>
<strong>Specify your Android testing dependencies</strong>. In order for the
<a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for Gradle</a> to
correctly build and run your UI Automator tests, you must specify the following libraries in
the {@code build.gradle} file of your Android app module:
<pre>
dependencies {
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.0.0'
}
</pre>
</li>
</ul>
<p>To optimize your UI Automator testing, you should first inspect the target apps UI components
and ensure that they are accessible. These optimization tips are described in the next two
sections.</p>
<h3 id="inspecting-ui">Inspecting the UI on a device</h3>
<p>Before designing your test, inspect the UI components that are visible on the device. To
ensure that your UI Automator tests can access these components, check that these components
have visible text labels,
<a href="http://developer.android.com/reference/android/view/View.html#attr_android:contentDescription">
{@code android:contentDescription}</a>
values, or both.</p>
<p>The {@code uiautomatorviewer} tool provides a convenient visual interface to inspect the layout
hierarchy and view the properties of UI components that are visible on the foreground of the device.
This information lets you create more fine-grained tests using UI Automator. For example, you can
create a UI selector that matches a specific visible property. </p>
<p>To launch the {@code uiautomatorviewer} tool:</p>
<ol>
<li>Launch the target app on a physical device.</li>
<li>Connect the device to your development machine.</li>
<li>Open a terminal window and navigate to the {@code &lt;android-sdk&gt;/tools/} directory.</li>
<li>Run the tool with this command:
<pre>$ uiautomatorviewer</pre>
</li>
</ol>
<p>To view the UI properties for your application:</p>
<ol>
<li>In the {@code uiautomatorviewer} interface, click the <strong>Device Screenshot</strong>
button.</li>
<li>Hover over the snapshot in the left-hand panel to see the UI components identified by the
{@code uiautomatorviewertool}. The properties are listed in the lower right-hand panel and the
layout hierarchy in the upper right-hand panel.</li>
<li>Optionally, click on the <strong>Toggle NAF Nodes</strong> button to see UI components that
are non-accessible to UI Automator. Only limited information may be available for these
components.</li>
</ol>
<p>To learn about the common types of UI components provided by Android, see
<a href="{@docRoot}guide/topics/ui/index.html">User Interface</a>.</p>
<h3>Ensuring your Activity is accessible</h3>
<p>The UI Automator test framework depends on the accessibility features of the Android framework
to look up individual UI elements. As a developer, you should implement these minimum
optimizations in your {@link android.app.Activity} to support UI Automator:</p>
<ul>
<li>Use the
<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
{@code android:contentDescription}</a>
attribute to label the {@link android.widget.ImageButton}, {@link android.widget.ImageView},
{@link android.widget.CheckBox} and other user interface controls.</li>
<li>Provide an <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">{@code android:hint}</a>
attribute instead of a content description for {@link android.widget.EditText} fields.</li>
<li>Associate an <a href="http://developer.android.com/reference/android/widget/TextView.html#attr_android:hint">
{@code android:hint}</a>
attribute with any graphical icons used by controls that provide feedback to the user
(for example, status or state information).</li>
<li>Use the {@code uiautomatorviewer} tool to ensure that the UI component is accessible to the
testing framework. You can also test the application by turning on accessibility services like
TalkBack and Explore by Touch, and try using your application using only directional controls.</li>
</ul>
<p>Generally, app developers get accessibility support for free, courtesy of
the {@link android.view.View} and {@link android.view.ViewGroup}
classes. However, some apps use custom view elements to provide a richer user experience. Such
custom elements won't get the accessibility support that is provided by the standard Android UI
elements. If this applies to your app, make sure that it exposes the custom-drawn UI element to
Android accessibility services by implementing the
{@link android.view.accessibility.AccessibilityNodeProvider} class.</p>
<p>If the custom view element contains a single element, make it accessible by
<a href="{@docRoot}guide/topics/ui/accessibility/apps.html#accessibility-methods">implementing
accessibility API methods</a>.
If the custom view contains elements that are not views themselves (for example, a
{@link android.webkit.WebView}, make sure it implements the
{@link android.view.accessibility.AccessibilityNodeProvider} class. For container views that
extend an existing container implementation
(for example, a {@link android.widget.ListView}), implementing
{@link android.view.accessibility.AccessibilityNodeProvider} is not necessary.</p>
<p>For more information about implementing and testing accessibility, see
<a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>.</p>
<h2 id="build">Create a UI Automator Test Class</h2>
<p>To build a UI Automator test, create a class that extends
{@link android.test.InstrumentationTestCase}. Implement the following programming model in your
UI Automator test class:</p>
<ol>
<li>Get a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html">{@code UiDevice}</a>
object to access the device you want to test, by calling the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html#getInstance(android.app.Instrumentation)">
{@code getInstance()}</a>
method and passing it an {@link android.app.Instrumentation} object as the argument.</li>
<li>Get a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
object to access a UI component that is displayed on the device
(for example, the current view in the foreground), by calling the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html#findObject(android.support.test.uiautomator.UiSelector)">
{@code findObject()}</a>
method.
</li>
<li>Simulate a specific user interaction to perform on that UI component, by calling a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
method; for example, call
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#performMultiPointerGesture(android.view.MotionEvent.PointerCoords[]...)">
{@code performMultiPointerGesture()}</a>
to simulate a multi-touch gesture, and
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#setText(java.lang.String)">{@code setText()}</a>
to edit a text field. You can call on the APIs in steps 2 and 3 repeatedly as necessary to test
more complex user interactions that involve multiple UI components or sequences of user actions.</li>
<li>Check that the UI reflects the expected state or behavior, after these user interactions are
performed. </li>
</ol>
<p>These steps are covered in more detail in the sections below.</p>
<h3 id="accessing-ui-components">Accessing UI Components</h3>
<p>The
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html">{@code UiDevice}</a>
object is the primary way you access and manipulate the state of the
device. In your tests, you can call
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html">{@code UiDevice}</a>
methods to check for the state of various properties, such as current orientation or display size.
Your test can use the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html">{@code UiDevice}</a>
object to perform device-level actions, such as forcing the device into a specific rotation,
pressing D-pad hardware buttons, and pressing the Home and Menu buttons.</p>
<p>Its good practice to start your test from the Home screen of the device. From the Home screen
(or some other starting location youve chosen in the device), you can call the methods provided by
the UI Automator API to select and interact with specific UI elements. </p>
<p>The following code snippet shows how your test might get an instance of
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html">{@code UiDevice}</a>
and simulate a Home button press:</p>
<pre>
import android.test.InstrumentationTestCase;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.By;
public class CalculatorUiTest extends InstrumentationTestCase {
private UiDevice mDevice;
public void setUp() {
// Initialize UiDevice instance
mDevice = UiDevice.getInstance(getInstrumentation());
// Start from the home screen
mDevice.pressHome();
mDevice.wait(Until.hasObject(By.pkg(getHomeScreenPackage()).depth(0)),
}
}
</pre>
<p>Use the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiDevice.html#findObject(android.support.test.uiautomator.UiSelector)">{@code findObject()}</a>
method to retrieve a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
which represents a view that matches a given selector criteria. You can reuse the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
instances that you have created in other parts of your app testing, as needed. Note that the
UI Automator test framework searches the current display for a match every time your test uses a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
instance to click on a UI element or query a property.</p>
<p>The following snippet shows how your test might construct
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
instances that represent a Cancel button and a OK button in an app.</p>
<pre>
UiObject cancelButton = mDevice.findObject(new UiSelector()
.text("Cancel"))
.className("android.widget.Button"));
UiObject okButton = mDevice.findObject(new UiSelector()
.text("OK"))
.className("android.widget.Button"));
// Simulate a user-click on the OK button, if found.
if(okButton.exists() &#38;&#38; okButton.isEnabled()) {
okButton.click();
}
</pre>
<h4 id="specifying-selector">Specifying a selector</h4>
<p>If you want to access a specific UI component in an app, use the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiSelector.html">{@code UiSelector}</a>
class. This class represents a query for specific elements in the
currently displayed UI. </p>
<p>If more than one matching element is found, the first matching element in the layout hierarchy
is returned as the target
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>.
When constructing a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiSelector.html">{@code UiSelector}</a>,
you can chain together multiple properties to refine your search. If no matching UI element is
found, a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObjectNotFoundException.html">
{@code UiAutomatorObjectNotFoundException}</a> is thrown. </p>
<p>You can use the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiSelector.html#childSelector(android.support.test.uiautomator.UiSelector)">{@code childSelector()}</a>
method to nest multiple
<a href="{@docRoot}reference/android/support/test/uiautomator/UiSelector.html">{@code UiSelector}</a>
instances. For example, the following code example shows how your test might specify a search to
find the first {@link android.widget.ListView} in the currently displayed UI, then search within that
{@link android.widget.ListView} to find a UI element with the text property Apps.</p>
<pre>
UiObject appItem = new UiObject(new UiSelector()
.className("android.widget.ListView")
.instance(1)
.childSelector(new UiSelector()
.text("Apps")));
</pre>
<p>As a best practice, when specifying a selector, you should use a Resource ID (if one is assigned
to a UI element) instead of a text element or content-descriptor. Not all elements have a text
element (for example, icons in a toolbar). Text selectors are brittle and can lead to test failures
if there are minor changes to the UI. They may also not scale across different languages; your text
selectors may not match translated strings.</p>
<p>It can be useful to specify the object state in your selector criteria. For example, if you want
to select a list of all checked elements so that you can uncheck them, call the
<a href="{@docRoot}reference/android/support/test/uiautomator/By.html#checked(boolean)">
{@code checked()}</a>
method with the argument set to {@code true}.</p>
<h3 id="performing-actions">Performing Actions</h3>
<p>Once your test has obtained a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
object, you can call the methods in the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>
class to perform user interactions on the UI component represented by that
object. You can specify such actions as:</p>
<ul>
<li>
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#click()">
{@code click()}</a>
: Clicks the center of the visible bounds of the UI element.</li>
<li>
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#dragTo(int, int, int)">
{@code dragTo()}</a>
: Drags this object to arbitrary coordinates.</li>
<li>
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#setText(java.lang.String)">
{@code setText()}</a>
: Sets the text in an editable field, after clearing the field's content.
Conversely, the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#clearTextField()">
{@code clearTextField()}</a>
method clears the existing text in an editable field.</li>
<li>
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#swipeUp(int)">
{@code swipeUp()}</a>
: Performs the swipe up action on the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html">{@code UiObject}</a>.
Similarly, the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#swipeDown(int)">
{@code swipeDown()}</a>,
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#swipeLeft(int)">
{@code swipeLeft()}</a>, and
<a href="{@docRoot}reference/android/support/test/uiautomator/UiObject.html#swipeRight(int)">
{@code swipeRight()}</a>
methods perform corresponding actions.</li>
</ul>
<p>The UI Automator testing framework allows you to send an
{@link android.content.Intent}
or launch an {@link android.app.Activity}
without using shell commands, by getting a
{@link android.content.Context}
object through
{@link android.app.Instrumentation#getContext() getContext()}.</p>
<p>The following snippet shows how your test can use an
{@link android.content.Intent} to launch the app under test. This approach is useful when you are
only interested in testing the calculator app, and don't care about the launcher.</p>
<pre>
public void setUp() {
...
// Launch a simple calculator app
Context context = getInstrumentation().getContext();
Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(CALC_PACKAGE);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Clear out any previous instances
context.startActivity(intent);
mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
}
</pre>
<h4 id="actions-on-collections">Performing actions on collections</h4>
<p>Use the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiCollection.html">
{@code UiCollection}</a>
class if you want to simulate user interactions on a
collection of items (for example, songs in a music album or a list of emails in an Inbox). To
create a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiCollection.html">
{@code UiCollection}</a>
object, specify a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiSelector.html">{@code UiSelector}</a>
that searches for a
UI container or a wrapper of other child UI elements, such as a layout view that contains child UI
elements.</p>
<p>The following code snippet shows how your test might construct a
<a href="{@docRoot}reference/android/support/test/uiautomator/UiCollection.html">
{@code UiCollection}</a>
to represent a video album that is displayed within a {@link android.widget.FrameLayout}:</p>
<pre>
UiCollection videos = new UiCollection(new UiSelector()
.className("android.widget.FrameLayout"));
// Retrieve the number of videos in this collection:
int count = videos.getChildCount(new UiSelector()
.className("android.widget.LinearLayout"));
// Find a specific video and simulate a user-click on it
UiObject video = videos.getChildByText(new UiSelector()
.className("android.widget.LinearLayout"), "Cute Baby Laughing");
video.click();
// Simulate selecting a checkbox that is associated with the video
UiObject checkBox = video.getChild(new UiSelector()
.className("android.widget.Checkbox"));
if(!checkBox.isSelected()) checkbox.click();
</pre>
<h4 id="actions-on-scrollable-views">Performing actions on scrollable views</h4>
<p>Use the
<a href="{@docRoot}reference/android/support/test/uiautomator/UiScrollable.html">
{@code UiScrollable}</a>
class to simulate vertical or horizontal scrolling across a display. This technique is helpful when
a UI element is positioned off-screen and you need to scroll to bring it into view.</p>
<p>The following code snippet shows how to simulate scrolling down the Settings menu and clicking
on an About tablet option:</p>
<pre>
UiScrollable settingsItem = new UiScrollable(new UiSelector()
.className("android.widget.ListView"));
UiObject about = settingsItem.getChildByText(new UiSelector()
.className("android.widget.LinearLayout"), "About tablet");
about.click();
</pre>
<h3 id="verifying-results">Verifying Results</h3>
<p>The {@link android.test.InstrumentationTestCase} extends {@link junit.framework.TestCase}, so
you can use standard JUnit <a href="http://junit.org/javadoc/latest/org/junit/Assert.html"
class="external-link">{@code Assert}</a> methods to test
that UI components in the app return the expected results. </p>
<p>The following snippet shows how your test can locate several buttons in a calculator app, click
on them in order, then verify that the correct result is displayed.</p>
<pre>
private static final String CALC_PACKAGE = "com.myexample.calc";
public void testTwoPlusThreeEqualsFive() {
// Enter an equation: 2 + 3 = ?
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("two")).click();
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("plus")).click();
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("three")).click();
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("equals")).click();
// Verify the result = 5
UiObject result = mDevice.findObject(By.res(CALC_PACKAGE, "result"));
assertEquals("5", result.getText());
}
</pre>
<h2 id="run">Run UI Automator Tests on a Device or Emulator</h2>
<p>UI Automator tests are based on the {@link android.app.Instrumentation} class. The
<a href="https://developer.android.com/tools/building/plugin-for-gradle.html">
Android Plug-in for Gradle</a>
provides a default directory ({@code src/androidTest/java}) for you to store the instrumented test
classes and test suites that you want to run on a device. The plug-in compiles the test
code in that directory and then executes the test app using a test runner class. You are
strongly encouraged to use the
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code AndroidJUnitRunner}</a>
class provided in the
<a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>
as your default test runner. </p>
<p>To run UI Automator tests in your Gradle project:</p>
<ol>
<li>Specify
<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code AndroidJUnitRunner}</a>
as the default test instrumentation runner in your {@code build.gradle} file:
<pre>
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}</pre>
</li>
<li>Run your tests from the command-line by calling the {@code connectedCheck}
(or {@code cC}) task:
<pre>./gradlew cC</pre>
</li>
</ol>

View File

@@ -1840,6 +1840,24 @@ results."
</ul>
</li>
</ul>
<ul>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/testing/ui-testing/index.html"
description="How to automate your user interface tests for Android apps.">
Automating UI Tests
</a></div>
<ul>
<li><a href="<?cs var:toroot ?>training/testing/ui-testing/espresso-testing.html">
<span class="en">Testing UI for a Single App</span>
</a>
</li>
<li><a href="<?cs var:toroot ?>training/testing/ui-testing/uiautomator-testing.html">
<span class="en">Testing UI for Multiple Apps</span>
</a>
</li>
</ul>
</li>
</ul>
</li>
<!-- End best Testing -->