Espresso

Espresso is a framework created by Google for Android that allows us to write tests on the user interface. It has a simple API, easily adapts to our needs and eliminates the complexity of managing different threads.

Espresso is based on Android instrumentation framework, which means the automation test is built into a single test apk. This test apk is different from normal application apk

Espresso Testing works basically in three blocks:

ViewMatchers– allows you to find an item in the view
ViewActions– allows executing actions on the elements
ViewAssertions – validate a view state

Working with espresso it is advisable to have below sheet nearby. –>
Espresso Guide

CheetSheet
CheetSheet

Configuration

To make Espresso Testing work in our project we need to add the dependencies in our ‘app / build.gradle’ file and a physical or virtual device.

dependencies {
 implementation fileTree(dir: 'libs', include: ['*.jar'])
 testImplementation 'junit:junit:4.12'

   // Android runner and rules support
 androidTestImplementation 'com.android.support.test:runner:1.0.2'
 androidTestImplementation 'com.android.support.test:rules:1.0.2'

   // Espresso
 androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

It is recommended to turn off the animations of our android device that we will use for the tests in the developer options. An easy way to do it is by executing these commands from your terminal:

adb shell settings put global window_animation_scale 0.0 
adb shell settings put global transition_animation_scale 0.0 
adb shell settings put global animator_duration_scale 0.0

Record_Espresso

For this we must go in the options bar of Android studio to Run -> Record Espresso Test
It will launch a screen where we can go making assertions.
And in the end we will generate the test code:

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

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

  @Test
  public void textViewToggleVisibilityTest() {
    ViewInteraction textView = onView(
        allOf(withId(R.id.tv_hello), withText("Hello buddy!"),
            childAtPosition(
                childAtPosition(
                    withId(android.R.id.content),
                    0),
                0),
            isDisplayed()));
    textView.check(matches(withText("Hello buddy!")));

    ViewInteraction appCompatButton = onView(
        allOf(withId(R.id.button), withText("click me"),
            childAtPosition(
                childAtPosition(
                    withId(android.R.id.content),
                    0),
                2),
            isDisplayed()));
    appCompatButton.perform(click());

    ViewInteraction textView2 = onView(
        allOf(withId(R.id.tv_see_you), withText("See you"),
            childAtPosition(
                childAtPosition(
                    withId(android.R.id.content),
                    0),
                1),
            isDisplayed()));
    textView2.check(matches(isDisplayed()));
  }

  private static Matcher childAtPosition(
      final Matcher parentMatcher, final int position) {

    return new TypeSafeMatcher() {
      @Override
      public void describeTo(Description description) {
        description.appendText("Child at position " + position + " in parent ");
        parentMatcher.describeTo(description);
      }

      @Override
      public boolean matchesSafely(View view) {
        ViewParent parent = view.getParent();
        return parent instanceof ViewGroup && parentMatcher.matches(parent)
            && view.equals(((ViewGroup) parent).getChildAt(position));
      }
    };
  }
}

Simple Manual form

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

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

  @Test
  public void textViewToggleVisibilityTest() {


    // here we are going to look in the view tree
    // we use allOff from hamcrest library to combine matchers
    ViewInteraction tvHello = onView(allOf(withId(R.id.tv_hello),withText(R.String.hello_buddy)));

    ViewInteraction tvSeeYou = onView(allOf(withId(R.id.tv_see_you),withText(R.String.see_you)));

    // assert that textView with text 'Hello buddy!' is display
    tvHello.check(matches(isDisplayed()));


    // assert that textView with text 'See you' has visibility gone
tvSeeYou.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));

    // find button with given id and click
    onView(withId(R.id.button)).perform(click());

    // assert see you is visible and hello buddy is not.        tvHello.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
    tvSeeYou.check(matches(isDisplayed()));
  }
}

We can verify that Espresso has automatically generated more code, which can be easily broken and will require more maintenance. For example, we used the literal string to find the view and in case of changing the language to the device the test would fail, however, we can manually use the id of the string resource and add as many matchers as we need.

This hamcrest reference guide can be useful when working with matchers.

Espresso is based on Android instrumentation framework, which means the automation test is built into a single test apk. This test apk is different from normal application apk

1. There is an instrumentation registered in AndroidManifest.xml, which will be registered to the Android system once test apk is installed

2. The test apk must be signed using the same signature with the application apk, in order to run automation test

3. The test apk runs in the same process as application apk

Above are the only requirements of any instrument based test framework has. So there is no dependency on source code.

But why we find most of the Espresso tutorials are mixed with source code? Because it will make the test simpler:

You can easily control the activity lifecycle using class ActivityTestRule.

You can test application-defined classes easily.

You can test UI widgets using widget id

On the contrary, you have to write lots of reflection code to get the classes you need if you don't compile with source code. For example:

You have to use Class.forName to load the entrance activity and launch it

You have to use Java reflection to test application-defined classes

You have to use literal information to find UI widgets because you don't have the id of the UI widgets

To sum up, it is OK to run Espresso automation test without application source code, but it's much harder and makes test codes ugly.

You can refer to the example project from AndroidTestWithoutSource.

Reference
https://apiumhub.com/tech-blog-barcelona/tag/ui-testing/