Thursday, March 24, 2016

Android Espresso UI Testing Notes

Android Espresso UI Testing Notes

There isn't much in official Google Android documentation. You'll have to play with a github sample to figure out how to apply it to your project.

for testing intents
http://collectiveidea.com/blog/archives/2015/06/11/testing-for-android-intents-using-espresso/

Good presentation by chiuki github
http://chiuki.github.io/advanced-android-espresso/#/

Espresso(Google)

Use Espresso to write concise, beautiful, and reliable Android UI tests like this:






@Test
public void greeterSaysHello() {
  onView(withId(R.id.name_field))
    .perform(typeText("Steve"));
  onView(withId(R.id.greet_button))
    .perform(click());
  onView(withText("Hello Steve!"))
    .check(matches(isDisplayed()));
}

Set up instructions
https://google.github.io/android-testing-support-library/docs/espresso/setup/index.html

Download Espresso

  • Make sure you have installed the latest Android Support Repository under Extras (see instructions).
  • Open your app’s build.gradle file. This is usually not the top-level build.gradle file but app/build.gradle.
  • Add the following lines inside dependencies:






androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'

  • See the downloads section for more artifacts (espresso-contrib, espresso-web, etc.)

Set the instrumentation runner

  • Add to the same build.gradle file the following line in android.defaultConfig:






testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

Example build.gradle file







apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22"

    defaultConfig {
        applicationId "com.my.awesome.app"
        minSdkVersion 10
        targetSdkVersion 22.0.1
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    // App's dependencies, including test
    compile 'com.android.support:support-annotations:22.2.0'

    // Testing-only dependencies
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2
// include espresso-intentsandroidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2'

Analytics

In order to make sure we are on the right track with each new release, the test runner collects analytics. More specifically, it uploads a hash of the package name of the application under test for each invocation. This allows us to measure both the count of unique packages using Espresso as well as the volume of usage.
If you do not wish to upload this data, you can opt out by passing the following argument to the test runner: disableAnalytics "true" (see how to pass custom arguments).

Add the first test

Android Studio creates tests by default in src/androidTest/java/com.example.package/
Example JUnit4 test using Rules:
(But this doesn't tell how to do an espresso test, see github example below, nor how or where to create test class .java file for each activity class)






@RunWith(AndroidJUnit4.class)
@LargeTest
public class HelloWorldEspressoTest {

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);

    @Test
    public void listGoesOverTheFold() {
        onView(withText("Hello world!")).check(matches(isDisplayed()));
    }
}

Running tests

In Android Studio

Create a test configuration
In Android Studio:
  • Open Run menu -> Edit Configurations
  • Add a new Android Tests configuration
  • Choose a module
  • Add a specific instrumentation runner:






  android.support.test.runner.AndroidJUnitRunner

Espresso Basics - how to tell Espresso to find a View

The Espresso API encourages test authors to think in terms of what a user might do while interacting with the application - locating UI elements and interacting with them. At the same time, the framework prevents direct access to activities and views of the application because holding on to these objects and operating on them off the UI thread is a major source of test flakiness. Thus, you will not see methods like getView and getCurrentActivity in the Espresso API. You can still safely operate on views by implementing your own ViewActions and ViewAssertions.
Here’s an overview of the main components of Espresso:
  • Espresso – Entry point to interactions with views (via onView and onData). Also exposes APIs that are not necessarily tied to any view (e.g.pressBack).
  • ViewMatchers – A collection of objects that implement Matcher<? super View> interface. You can pass one or more of these to the onViewmethod to locate a view within the current view hierarchy.
  • ViewActions – A collection of ViewActions that can be passed to the ViewInteraction.perform() method (for example, click()).
  • ViewAssertions – A collection of ViewAssertions that can be passed the ViewInteraction.check() method. Most of the time, you will use the matches assertion, which uses a View matcher to assert the state of the currently selected view. (continues at link)
Advanced Samples




Cheat sheet: https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/index.html

Espresso cheat-sheet

Walkthrough Working Sample Project

This github has a working espresso project

https://github.com/googlesamples/android-testing

Only the top level has a "download ZIP button", but sometimes the zip file has no files in it, only directory so use the git clone function (see below)






Takes you here




Download the file
Run the file


Gets you here:
Click on the plus +










This is the main activity with two buttons to test.

activity_main.xml - design view


activity_show_text.xml





Screens comes up ready to type. Type something. Hit the down arrow at the bottom to exit keyboard and leave text in the blank.


Change Text: - changes Hello Espresso to the new text .

Open activity and change text - brings up another activity with the next text in it. 
Hit the left triangle to to back to main


Mainactivity.java

/* * Copyright 2015, The Android Open Source Project * 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*      http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and *
 limitations under the License. */

package com.example.android.testing.espresso.BasicSample;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

/** * An {@link Activity} that gets a text string from the user and 
displays it back when the user * clicks on one of the two buttons. 
The first one shows it in the same activity and the second
one opens another activity and displays the message. */

public class MainActivity extends Activity implements View.OnClickListener {

    // The TextView used to display the message inside the Activity.    
    private TextView mTextView;

    // The EditText where the user types the message.    
    private EditText mEditText;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Set the listeners for the buttons.        
        findViewById(R.id.changeTextBt).setOnClickListener(this);
        findViewById(R.id.activityChangeTextBtn).setOnClickListener(this);

        mTextView = (TextView) findViewById(R.id.textToBeChanged);
        mEditText = (EditText) findViewById(R.id.editTextUserInput);
    }

    @Override    public void onClick(View view) {
        // Get the text from the EditText view.        

        final String text = mEditText.getText().toString();

        switch (view.getId()) {
            case R.id.changeTextBt:
                // First button's interaction: set a text in a text view.                
                mTextView.setText(text);
                break;
            case R.id.activityChangeTextBtn:
                // Second button's interaction: start an activity and send a message to it.
                Intent intent = ShowTextActivity.newStartIntent(this, text);
                startActivity(intent);
                break;
        }
    }
}


The test code is Android View under com.example.android.testing.espresso.BasicSample(androidTest)
changeTextBehaviorTest.java




changeTextBehaviorTest.java
/* * Copyright 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package com.example.android.testing.espresso.BasicSample;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import android.app.Activity;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

/** * Basic tests showcasing simple view matchers and actions like {@link ViewMatchers#withId},
 * {@link ViewActions#click} and {@link ViewActions#typeText}. 
* <p>
 * Note that there is no need to tell Espresso that a view is in a different {@link Activity}.
 */

@RunWith(AndroidJUnit4.class)@LargeTest
public class ChangeTextBehaviorTest {

    public static final String STRING_TO_BE_TYPED = "Espresso";

    /**     * A JUnit {@link Rule @Rule} to launch your activity under test. This is a replacement     * for {@link ActivityInstrumentationTestCase2}.     * <p>
     * Rules are interceptors which are executed for each test method and will run before     * any of your setup code in the {@link Before @Before} method.     * <p>
     * {@link ActivityTestRule} will create and launch of the activity for you and also expose     * the activity under test. To get a reference to the activity you can use     * the {@link ActivityTestRule#getActivity()} method.     */    @Rule
    // Main Activity is the class to make tests for
    // Each Activity will have a different test class file
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
            MainActivity.class);

    // this is one of two tests
    @Test
    public void changeText_sameActivity() {
        // Type text and then press the button.        
        onView(withId(R.id.editTextUserInput))
                .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());
        onView(withId(R.id.changeTextBt)).perform(click());

        // Check that the text was changed.        
        onView(withId(R.id.textToBeChanged)).check(matches(withText(STRING_TO_BE_TYPED)));
    }

    @Test
    public void changeText_newActivity() {
        // Type text and then press the button.        
        onView(withId(R.id.editTextUserInput)).perform(typeText(STRING_TO_BE_TYPED),
                closeSoftKeyboard());
        onView(withId(R.id.activityChangeTextBtn)).perform(click());

        // This view is in a different Activity, no need to tell Espresso.        
        onView(withId(R.id.show_text_view)).check(matches(withText(STRING_TO_BE_TYPED)));
    }

Build.gradle


apply plugin: 'com.android.application'


android {
  compileSdkVersion 23
  buildToolsVersion '23.0.1'
  defaultConfig {
      applicationId "com.example.android.testing.espresso.BasicSample"
      minSdkVersion 10
      targetSdkVersion 23
      versionCode 1
      versionName "1.0"


      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  }
  lintOptions {
      abortOnError false
  }
  productFlavors {
  }
}


dependencies {
  // App dependencies
  compile 'com.android.support:support-annotations:23.0.1'
  compile 'com.google.guava:guava:18.0'
  // Testing-only dependencies
  // Force usage of support annotations in the test app, since it is internally used by the runner module.
  androidTestCompile 'com.android.support:support-annotations:23.0.1'
  androidTestCompile 'com.android.support.test:runner:0.4.1'
  androidTestCompile 'com.android.support.test:rules:0.4.1'
  androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
}


To run the app: in the run menu you can pick whether to run the app, or one of the available tests



Connect the device. You'll need to
  • Install generic or  specific USB debugging driver onto your pc 
  • Enable developer options on device - settings, build version, tap until you can be developer
  • Developer settings shows up - check enable USB debugging
  • Hook up device to PC




Creating project, there are two directories for the app and for tests (AndroidTest)

java
    com.example.ahu.espressofirst
      MainActivity

    com.example.ahu.espressofirst (androidTest)
       ApplicationTest



Developing Android unit and instrumentation tests - Tutorial
www.vogella.com/tutorials/AndroidTesting/article.html
Sep 9, 2015 - Links are provides for other test frameworks like Espresso. ... Instrumented tests for testing Java classes which use the Android API. 5. Androidproject structure and test folder creation ... The Android test system has been updated to be based on JUnit 4 and you can run unit tests either on the Java virtual ...

5.1. Android project organization for tests

The preferred way of organizing tests is based on a convention. In your application project you should use the following base folder structure for your code organization, this is also the structure the project wizard creates.
  • app/src/main/java - for your source code of your main application build
  • app/src/test/java - for any unit test which can run on the JVM
  • app/src/androidTest/java - for any test which should run on an Android device
If you follow this conversion than the Android build system can automatically run the unit tests on the JVM and the Android tests on the Android device.

Warning To make Android Studio aware of your new dependency for unit or instrumentation tests you may have to select Unit Tests or Android Instrumentation Tests from the Build Variant view in Android Studio.

Level 23 
http://developer.android.com/reference/android/test/suitebuilder/package-summary.html

Classes



ActivityInstrumentationTestCase<T extends Activity>This class was deprecated in API level 3. new tests should be written usingActivityInstrumentationTestCase2, which provides more options for configuring the Activity under test  
ActivityInstrumentationTestCase2<T extends Activity>This class provides functional testing of a single activity. 
ActivityTestCaseThis is common code used to support Activity test cases. 
ActivityUnitTestCase<T extends Activity>This class provides isolated testing of a single activity. 
AndroidTestCaseExtend this if you need to access Resources or other things that depend on Activity Context. 
AndroidTestRunner
ApplicationTestCase<T extends Application>This test case provides a framework in which you can test Application classes in a controlled environment. 
InstrumentationTestCaseA test case that has access to Instrumentation
InstrumentationTestRunnerAn Instrumentation that runs various types of TestCases against an Android package (application). 
InstrumentationTestSuiteTestSuite that injects Instrumentation into InstrumentationTestCase before running them. 
IsolatedContextA mock context which prevents its users from talking to the rest of the device while stubbing enough methods to satify code that tries to talk to other packages. 
LoaderTestCaseA convenience class for testing Loaders. 
MoreAssertsContains additional assertion methods not found in JUnit. 
ProviderTestCase<T extends ContentProvider>This class was deprecated in API level 3. this class extends InstrumentationTestCase but should extend AndroidTestCase. Use ProviderTestCase2, which corrects this problem, instead.  
ProviderTestCase2<T extends ContentProvider>This test case class provides a framework for testing a single ContentProvider and for testing your app code with an isolated content provider. 
RenamingDelegatingContextThis is a class which delegates to the given context, but performs database and file operations with a renamed database/file name (prefixes default names with a given prefix). 
ServiceTestCase<T extends Service>This test case provides a framework in which you can test Service classes in a controlled environment. 
SingleLaunchActivityTestCase<T extends Activity>If you would like to test a single activity with an InstrumentationTestCase, this provides some of the boiler plate to launch and finish the activity in setUp() and tearDown()
SyncBaseInstrumentationIf you would like to test sync a single provider with an InstrumentationTestCase, this provides some of the boiler plate in setUp() and tearDown()
TouchUtilsReusable methods for generating touch events. 
ViewAssertsSome useful assertions about views. 



Level 19 
http://cs.szpt.edu.cn/android/reference/android/test/package-summary.html
http://cs.szpt.edu.cn/android/reference/android/test/ActivityInstrumentationTestCase2.html

ActivityInstrumentationTestCase2



java.lang.Object
   ↳junit.framework.Assert
   ↳junit.framework.TestCase
   ↳android.test.InstrumentationTestCase
   ↳android.test.ActivityTestCase
   ↳android.test.ActivityInstrumentationTestCase2<T extends android.app.Activity>

Class Overview


This class provides functional testing of a single activity. The activity under test will be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity()) and you will then be able to manipulate your Activity directly.
Other options supported by this test case include:
This class replaces ActivityInstrumentationTestCase, which is deprecated. New tests should be written using this base class.
If you prefer an isolated unit test, see ActivityUnitTestCase.

Deprecated ActivityInstrumentationTestCase2


ActivityInstrumentationTestCase2 replaces {@link android.test.ActivityInstrumentationTestCase}, which is deprecated.


ActivityInstrumentationTestCase2 or ServiceTestCase are deprecated in favor of ActivityTestRule or ServiceTestRule.


JUnit4 Rules with the ATSL - Google
https://google.github.io/android-testing-support-library/docs/rules/
Android Testing Support Library we are providing a set of JUnit rules to be used with ... are deprecated in favor of ActivityTestRule or ServiceTestRule .

With the Android Testing Support Library we are providing a set of JUnit rules to be used with the AndroidJUnitRunner. JUnit rules provide more flexibility and reduce the boilerplate code required in tests.
TestCases like ActivityInstrumentationTestCase2 or ServiceTestCase are deprecated in favor of ActivityTestRule or ServiceTestRule.

ActivityTestRule

This rule provides functional testing of a single activity. The activity under test will be launched before each test annotated with @Test and before any method annotated with @Before. It will be terminated after the test is completed and all methods annotated with @After are finished. The Activity under Test can be accessed during your test by calling ActivityTestRule#getActivity().













@RunWith(AndroidJUnit4.class)
@LargeTest
public class MyClassTest {

    @Rule
    public ActivityTestRule<MyClass> mActivityRule = new ActivityTestRule(MyClass.class);

    @Test
    public void myClassMethod_ReturnsTrue() { ... }
}



if  you're actually going for Espresso 2.1. If that is true, also note that:
TestCases like ActivityInstrumentationTestCase2 or ServiceTestCase are deprecated in favor of ActivityTestRule or ServiceTestRule.
As it can also be seen here or here, the annotations are to be used in the following way: (will adjust your deprecated test class just because I'm nice)
//annotate JUnit4 test classes with
@RunWith(AndroiJunit4.class)
@LargeTest
public class FirstActivityTest {

//use the following annotation and declare an ActivityTestRule for your activity under test
@Rule
public ActivityTestRule<FirstActivity> mActivityRule = new ActivityTestRule(FirstActivity.class);

//use @Before to setup your test fixture
@Before
public void setUp() { ... }

//annotate all test methods with
@Test
public void testHelloWorld() { ... }

//release resources by using
@After
public void tearDown() { ... }
}

No comments:

Post a Comment