From 34fb906c1aedd11a8a942fa068421153e1dccdc2 Mon Sep 17 00:00:00 2001 From: Eric Schmidt Date: Wed, 14 Sep 2016 18:18:50 -0700 Subject: docs: Adding Espresso Intent and Web docs Bug: 20721203 Bug: 20720633 bug: 27453733 Change-Id: I67a7d09a6652e3463728691f2a1f5679e900e776 --- .../testing/ui-testing/espresso-testing.jd | 984 +++++++++++++-------- 1 file changed, 608 insertions(+), 376 deletions(-) diff --git a/docs/html/training/testing/ui-testing/espresso-testing.jd b/docs/html/training/testing/ui-testing/espresso-testing.jd index d3d31debc353..3d2bca7748ee 100644 --- a/docs/html/training/testing/ui-testing/espresso-testing.jd +++ b/docs/html/training/testing/ui-testing/espresso-testing.jd @@ -24,17 +24,11 @@ trainingnavtop=true
    -
  1. - Set Up Espresso -
  2. +
  3. Set Up Espresso
  4. -
  5. - Create an Espresso Test Class -
  6. +
  7. Create an Espresso Test Class
  8. -
  9. - Run Espresso Tests on a Device or Emulator -
  10. +
  11. Run Espresso Tests on a Device or Emulator

@@ -59,31 +53,40 @@ trainingnavtop=true class="external-link">Android Testing Codelab - +

Testing user interactions within a single app helps 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 + 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.

The Espresso testing framework, provided by the Android Testing Support Library, - 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. + provides APIs for writing UI tests to simulate + user interactions within a + single target app. Espresso tests can run on + devices running Android 2.3.3 (API level 10) 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 add any timing workarounds, + such as {@link java.lang.Thread#sleep(long) Thread.sleep()} + in your test code.

- The Espresso testing framework is an instrumentation-based API and works - with the + The Espresso testing framework is + an instrumentation-based API and works with the {@code AndroidJUnitRunner} test runner.

@@ -92,105 +95,139 @@ trainingnavtop=true Set Up Espresso

-

Before building your UI test with Espresso, make sure to configure your test source code -location and project dependencies, as described in - -Getting Started with Testing.

+

+ Before building your UI test with Espresso, + make sure to configure your test source code + location and project dependencies, as described in + Getting Started with Testing. +

-

In the {@code build.gradle} file of your Android app module, you must set a dependency - reference to the Espresso library:

+

+ In the {@code build.gradle} file of your Android app + module, you must set a dependency + reference to the Espresso library: +

 dependencies {
-    ...
-    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
+    // Other dependencies ...
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
 }
 
-

Turn off animations on your test device — leaving system animations turned on in the test -device might cause unexpected results or may lead your test to fail. Turn off animations from -Settings by opening Developing Options and turning all the following options off: +

+ Turn off animations on your test device — + leaving system animations turned on in the test + device might cause unexpected results or may + lead your test to fail. Turn off animations from + Settings by opening Developer options + and turning all the following options off:

- - - -

If you want to set up your project to use Espresso features other than what the core API +

+ If you want to set up your project to use Espresso + features other than what the core API provides, see this resource.

-

- Create an Espresso Test Class -

+ -

- To create an Espresso test, create a Java class that follows this programming model: -

+

+ Create an Espresso Test Class +

-
    -
  1. 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 - - {@code onView()} method, or the - - {@code onData()} method for {@link android.widget.AdapterView} controls. -
  2. - -
  3. Simulate a specific user interaction to perform on that UI component, by calling the - {@code ViewInteraction.perform()} - or - {@code DataInteraction.perform()} - 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. -
  4. - -
  5. Repeat the steps above as necessary, to simulate a user flow across multiple - activities in the target app. -
  6. - -
  7. Use the +

    + To create an Espresso test, create a Java + class that follows this programming model: +

    + +
      +
    1. + 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 + + {@code onView()} method, or the + + {@code onData()} method for {@link android.widget.AdapterView} controls. +
    2. + +
    3. + Simulate a specific user interaction to + perform on that UI component, by calling the + {@code ViewInteraction.perform()} + or + {@code DataInteraction.perform()} + 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. +
    4. + +
    5. + Repeat the steps above as necessary, to simulate a user flow across multiple + activities in the target app. +
    6. + +
    7. + Use the {@code ViewAssertions} - methods to check that the UI reflects the expected - state or behavior, after these user interactions are performed. -
    8. -
    + methods to check that the UI reflects the expected + state or behavior, after these user interactions are performed. +
  8. +
-

- These steps are covered in more detail in the sections below. -

+

+ These steps are covered in more detail in the sections below. +

-

- The following code snippet shows how your test class might invoke this basic workflow: -

+

+ The following code snippet shows how your test + class might invoke this basic workflow: +

 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
 
-

Using Espresso with ActivityTestRule

+ + + +

+ Using Espresso with ActivityTestRule +

+

-The following section describes how to create a new Espresso test in the JUnit 4 style and use - -{@code ActivityTestRule} to reduce the amount of boilerplate code you need to write. By using - -{@code ActivityTestRule}, the testing framework launches the activity under test -before each test method annotated with @Test and before any method annotated with -@Before. The framework handles shutting down the activity after the test finishes -and all methods annotated with @After are run.

+ The following section describes how to create a + new Espresso test in the JUnit 4 style and use + {@code ActivityTestRule} + to reduce the amount of boilerplate code you need to write. By using + {@code ActivityTestRule}, + the testing framework launches the activity under test + before each test method annotated with + @Test and before any method annotated with + @Before. The framework handles + shutting down the activity after the test finishes + and all methods annotated with @After are run. +

 package com.example.android.testing.espresso.BasicSample;
@@ -234,103 +271,57 @@ public class ChangeTextBehaviorTest {
 }
 
-

- Using Espresso with ActivityInstrumentationTestCase2 -

-

The following section describes how to migrate to Espresso if you have existing test classes -subclassed from {@link android.test.ActivityInstrumentationTestCase2} and you don't want to rewrite -them to use JUnit4.

-

Note: For new UI tests, we strongly recommend that you write your -test in the JUnit 4 style and use the - -{@code ActivityTestRule} class, instead of -{@link android.test.ActivityInstrumentationTestCase2}.

-

- 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 - {@code AndroidJUnitRunner} - test runner. -

+ -

- To do this, call the - {@link android.test.InstrumentationTestCase#injectInstrumentation(android.app.Instrumentation) injectInstrumentation()} - method and pass in the result of - - {@code InstrumentationRegistry.getInstrumentation()}, as shown in the following code - example: -

- -
-import android.support.test.InstrumentationRegistry;
+

+ Accessing UI Components +

-public class MyEspressoTest - extends ActivityInstrumentationTestCase2<MyActivity> { - - private MyActivity mActivity; - - public MyEspressoTest() { - super(MyActivity.class); - } - - @Before - public void setUp() throws Exception { - super.setUp(); - injectInstrumentation(InstrumentationRegistry.getInstrumentation()); - mActivity = getActivity(); - } - - ... -} -
- -

Note: Previously, {@link android.test.InstrumentationTestRunner} -would inject the {@link android.app.Instrumentation} instance, but this test runner is being -deprecated.

- -

- Accessing UI Components -

- -

- Before Espresso can interact with the app under test, you must first specify the UI component - or view. Espresso supports the use of -Hamcrest matchers - for specifying views and adapters in your app. -

+

+ Before Espresso can interact with the app + under test, you must first specify the UI component + or view. Espresso supports the use of + Hamcrest matchers + for specifying views and adapters in your app. +

-

- To find the view, call the - {@code onView()} - method and pass in a view matcher that specifies the view that you are targeting. This is - described in more detail in Specifying a View Matcher. - The - {@code onView()} method returns a - - {@code ViewInteraction} - object that allows your test to interact with the view. - However, calling the - {@code onView()} 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 - Locating a view in an AdapterView instead. -

+

+ To find the view, call the + {@code onView()} + method and pass in a view matcher that + specifies the view that you are targeting. This is + described in more detail in + Specifying a View Matcher. + The {@code onView()} + method returns a + {@code ViewInteraction} + object that allows your test to interact with the view. + However, calling the + + {@code onView()} + method may not work if you want to locate a view in + an {@link android.support.v7.widget.RecyclerView} layout. + In this case, follow the instructions in + Locating a view in an AdapterView + instead. +

-

- Note: The - {@code onView()} 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 - - {@code NoMatchingViewException}. -

+

+ Note: + The {@code onView()} + 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 + {@code NoMatchingViewException}. +

-

- 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. -

+

+ 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. +

 public void testChangeText_sameActivity() {
@@ -344,231 +335,464 @@ public void testChangeText_sameActivity() {
 }
 
-

- Specifying a View Matcher -

+ +

+ Specifying a View Matcher +

-

- You can specify a view matcher by using these approaches: -

+

+ You can specify a view matcher by using these approaches: +

- -

- Locating a view in an AdapterView -

+

+ 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. +

-

- 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 - - {@code onView()} method might not work because only a - subset of the views may be loaded in the current view hierarchy. -

+ -

- Instead, call the {@code onData()} - method to obtain a - - {@code DataInteraction} - 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. -

+

+ Locating a view in an AdapterView +

+ +

+ 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 + {@code onView()} + method might not work because only a + subset of the views may be loaded in the current view hierarchy. +

-

- Note: The +

+ Instead, call the {@code onData()} - 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 - - {@code NoMatchingViewException}. -

+ method to obtain a + {@code DataInteraction} + 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. +

-

- The following code snippet shows how you can use the - {@code onData()} - 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}. -

+

+ Note: The + {@code onData()} + method does not check 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 + {@code NoMatchingViewException}. +

+ +

+ The following code snippet shows how you can use the + {@code onData()} + 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}. +

 onData(allOf(is(instanceOf(Map.class)),
-        hasEntry(equalTo(LongListActivity.ROW_TEXT), is(str))));
+        hasEntry(equalTo(LongListActivity.ROW_TEXT), is("test input")));
 
-

- Performing Actions -

+ -

- Call the {@code ViewInteraction.perform()} - or - {@code DataInteraction.perform()} - methods to - simulate user interactions on the UI component. You must pass in one or more - {@code ViewAction} - objects as arguments. Espresso fires each action in sequence according to - the given order, and executes them in the main thread. -

+

Performing Actions

-

- The - {@code ViewActions} - 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 {@code ViewAction} - objects. You can specify such actions as: -

+

+ Call the + {@code ViewInteraction.perform()} + or + {@code DataInteraction.perform()} + methods to + simulate user interactions on the UI component. You must pass in one or more + {@code ViewAction} + objects as arguments. Espresso fires each action in sequence according to + the given order, and executes them in the main thread. +

- +

+ The + {@code ViewActions} + 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 + {@code ViewAction} + objects. You can specify such actions as: +

-

- If the target view is inside a {@link android.widget.ScrollView}, perform the - {@code ViewActions.scrollTo()} - action first to display the view in the screen before other proceeding - with other actions. The - {@code ViewActions.scrollTo()} - action will have no effect if the view is already displayed. -

+ -

- Verifying Results -

+

+ If the target view is inside a {@link android.widget.ScrollView}, + perform the + {@code ViewActions.scrollTo()} + action first to display the view in the screen before other proceeding + with other actions. The + {@code ViewActions.scrollTo()} + action will have no effect if the view is already displayed. +

-

- Call the - {@code ViewInteraction.check()} - or - {@code DataInteraction.check()} - method to assert - that the view in the UI matches some expected state. You must pass in a - - {@code ViewAssertion} object as the argument. If the assertion fails, Espresso throws - an {@link junit.framework.AssertionFailedError}. -

+ -

- The +

+ Test your activities in isolation with Espresso Intents +

+ +

+ Espresso Intents + enables validation and stubbing of intents sent out by an app. + With Espresso Intents, you can test an app, activity, or service in isolation + by intercepting outgoing intents, stubbing the result, and sending it back to + the component under test. +

+ +

+ To begin testing with Espresso Intents, you need + to add the following line to your app's build.gradle file: +

+ +
+dependencies {
+  // Other dependencies ...
+  androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
+}
+
+ +

+ To test an intent, you need to create an instance of the + IntentsTestRule + class, which is very similar to the + ActivityTestRule + class. + The + IntentsTestRule + class initializes Espresso Intents before each test, + terminates the host activity, + and releases Espresso Intents after each test. +

+ +

+ The test class shown in the following codes snippet provides a simple + test for an explicit intent. It tests the activities and intents created in the + Building Your First App + tutorial. +

+ +
+@Large
+@RunWith(AndroidJUnit4.class)
+public class SimpleIntentTest {
+
+    private static final String MESSAGE = "This is a test";
+    private static final String PACKAGE_NAME = "com.example.myfirstapp";
+
+    /* Instantiate an IntentsTestRule object. */
+    @Rule
+    public IntentsTestRule≶MainActivity> mIntentsRule =
+      new IntentsTestRule≶>(MainActivity.class);
+
+    @Test
+    public void verifyMessageSentToMessageActivity() {
+
+        // Types a message into a EditText element.
+        onView(withId(R.id.edit_message))
+                .perform(typeText(MESSAGE), closeSoftKeyboard());
+
+        // Clicks a button to send the message to another
+        // activity through an explicit intent.
+        onView(withId(R.id.send_message)).perform(click());
+
+        // Verifies that the DisplayMessageActivity received an intent
+        // with the correct package name and message.
+        intended(allOf(
+                hasComponent(hasShortClassName(".DisplayMessageActivity")),
+                toPackage(PACKAGE_NAME),
+                hasExtra(MainActivity.EXTRA_MESSAGE, MESSAGE)));
+
+    }
+}
+
+ +

+ For more information about Espresso Intents, see the + Espresso Intents + documentation on the Android Testing Support Library site. + You can also download the + IntentsBasicSample and + IntentsAdvancedSample + code samples. +

+ + + +

+ Testing WebViews with Espresso Web +

+ +

+ Espresso Web allows you to test {@link android.webkit.WebView} components + contained within an activity. It uses the + WebDriver API to inspect and control the + behavior of a {@link android.webkit.WebView}. +

+ +

+ To begin testing with Espresso Web, you need + to add the following line to your app's build.gradle file: +

+ +
+dependencies {
+  // Other dependencies ...
+  androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
+}
+
+ +

+ When creating a test using Espresso Web, you need to enable + JavaScript on the {@link android.webkit.WebView} when you instantiate the + ActivityTestRule + object to test the activity. In the tests, you can select + HTML elements displayed in the + {@link android.webkit.WebView} and simulate user interactions, like + entering text into a text box and then clicking a button. After the actions + are completed, you can then verify that the results on the + Web page match the results that you expect. +

+ +

+ In the following code snippet, the class tests + a {@link android.webkit.WebView} component with the id value 'webview' + in the activity being tested. + The verifyValidInputYieldsSuccesfulSubmission() test selects an + <input> element on the + Web page, enters some text, and checks text that appears in + another element. +

+ +
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class WebViewActivityTest {
+
+    private static final String MACCHIATO = "Macchiato";
+    private static final String DOPPIO = "Doppio";
+
+    @Rule
+    public ActivityTestRule mActivityRule =
+        new ActivityTestRule(WebViewActivity.class,
+            false /* Initial touch mode */, false /*  launch activity */) {
+
+        @Override
+        protected void afterActivityLaunched() {
+            // Enable JavaScript.
+            onWebView().forceJavascriptEnabled();
+        }
+    }
+
+    @Test
+    public void typeTextInInput_clickButton_SubmitsForm() {
+       // Lazily launch the Activity with a custom start Intent per test
+       mActivityRule.launchActivity(withWebFormIntent());
+
+       // Selects the WebView in your layout.
+       // If you have multiple WebViews you can also use a
+       // matcher to select a given WebView, onWebView(withId(R.id.web_view)).
+       onWebView()
+           // Find the input element by ID
+           .withElement(findElement(Locator.ID, "text_input"))
+           // Clear previous input
+           .perform(clearElement())
+           // Enter text into the input element
+           .perform(DriverAtoms.webKeys(MACCHIATO))
+           // Find the submit button
+           .withElement(findElement(Locator.ID, "submitBtn"))
+           // Simulate a click via JavaScript
+           .perform(webClick())
+           // Find the response element by ID
+           .withElement(findElement(Locator.ID, "response"))
+           // Verify that the response page contains the entered text
+           .check(webMatches(getText(), containsString(MACCHIATO)));
+    }
+}
+
+ +

+ For more information about Espresso Web, see the + Espresso + Web documentation on the Android Testing Support Library site.. + You can also download this code snippet as part of the + Espresso Web code sample. +

+ + + +

+ Verifying Results +

+ +

+ Call the + {@code ViewInteraction.check()} + or + {@code DataInteraction.check()} + method to assert + that the view in the UI matches some expected state. You must pass in a + {@code ViewAssertion} + object as the argument. If the assertion fails, Espresso throws + an {@link junit.framework.AssertionFailedError}. +

+ +

+ The {@code ViewAssertions} - class provides a list of helper methods for specifying common - assertions. The assertions you can use include: -

+ class provides a list of helper methods for specifying common + assertions. The assertions you can use include: +

- + + +

+ 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. +

-

- 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. -

 public void testChangeText_sameActivity() {
     // Type text and then press the button.
@@ -580,14 +804,22 @@ public void testChangeText_sameActivity() {
 }
 
-

Run Espresso Tests on a Device or Emulator

+ + +

+ Run Espresso Tests on a Device or Emulator +

+

-You can run Espresso tests from Android Studio or -from the command-line. Make sure to specify - - {@code AndroidJUnitRunner} as the default instrumentation runner in your project. + You can run Espresso tests from + Android Studio or + from the command-line. Make sure to specify + {@code AndroidJUnitRunner} + as the default instrumentation runner in your project.

+

-To run your Espresso test, follow the steps for running instrumented tests -described in -Getting Started with Testing.

+ To run your Espresso test, follow the steps for running instrumented tests + described in + Getting Started with Testing. +

-- cgit v1.2.3-59-g8ed1b