Android Testing with Espresso

Espresso is Android UI testing support library. It’s small and easy to use. Here what we can do with Expresso.

espresso-cheat-sheet-2-1-0-1

https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/

Create new project
Create simple login screen look like below called LoginActivity.

Screenshot 2016-07-10 09.20.46

Here activity_login.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.bananacoding.expressologin.LoginActivity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <EditText
                    android:id="@+id/tv_email"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/prompt_email"
                    android:inputType="textEmailAddress"
                    android:maxLines="1"
                    android:singleLine="true" />

            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <EditText
                    android:id="@+id/tv_password"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/prompt_password"
                    android:imeActionId="@+id/login"
                    android:imeActionLabel="@string/action_login"
                    android:imeOptions="actionUnspecified"
                    android:inputType="textPassword"
                    android:maxLines="1"
                    android:singleLine="true" />

            </android.support.design.widget.TextInputLayout>

            <Button
                android:id="@+id/btn_login"
                style="?android:textAppearanceSmall"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="@string/action_login"
                android:textStyle="bold" />

        </LinearLayout>

    </ScrollView>

</LinearLayout>

Setup Expresso

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

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

Create test class called LoginActivityTest and runner annotation.

@RunWith(AndroidJUnit4.class)
public class LoginActivity {

}

Add activity test rule.

@Rule
public ActivityTestRule<LoginActivity> activityRule = new ActivityTestRule<>(LoginActivity.class);

*Note make sure you create the test class at src/androidTest/java not src/test/java

Let’s add the test

When we do not fill anything in email and password then press the login button, it will show the error message.

Screenshot 2016-07-10 09.53.45

You will think, Expresso does not have ViewMatchers for error message. How we check the error message of the EditText?

If it does not have, just create it!.
Create new custom view matchers called withError.

    private static Matcher withError(final String expected) {
        return new TypeSafeMatcher() {
            @Override
            protected boolean matchesSafely(View item) {
                if (item instanceof EditText) {
                    return ((EditText)item).getError().toString().equals(expected);
                }
                return false;
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("Not found error message [" + expected + "]");
            }
        };
    }

We need to implement matchesSafely method, to match error message of EditText.

So we can use it write a test like this.

    @Test
    public void emailIsEmpty() {
        onView(withId(R.id.tv_email)).perform(clearText());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withId(R.id.tv_email)).check(matches(withError("This field is required")));
    }

Also, when password is empty.

Screenshot 2016-07-10 13.29.34

    @Test
    public void passwordIsEmpty() {
        onView(withId(R.id.tv_email)).perform(typeText("email@email.com"), closeSoftKeyboard());
        onView(withId(R.id.tv_password)).perform(clearText());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withId(R.id.tv_email)).check(matches(withError("This field is required")));
    }

When email is invalid, it will show the error message.

Screenshot 2016-07-10 09.56.16

    @Test
    public void emailIsInvalid() {
        onView(withId(R.id.tv_email)).perform(typeText("invalid"), closeSoftKeyboard());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withId(R.id.tv_email)).check(matches(withError("This email address is invalid")));
    }

When password is too short, it will show the error message.

Screenshot 2016-07-10 09.56.43

    @Test
    public void passwordIsTooShort() {
        onView(withId(R.id.tv_email)).perform(typeText("email@email.com"), closeSoftKeyboard());
        onView(withId(R.id.tv_password)).perform(typeText("1234"), closeSoftKeyboard());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withId(R.id.tv_password)).check(matches(withError("This password is too short")));
    }

When username or password incorrect, will show toast message.

Screenshot 2016-07-10 10.45.20

    @Test
    public void loginFailed() {
        onView(withId(R.id.tv_email)).perform(typeText("incorrect@email.com"), closeSoftKeyboard());
        onView(withId(R.id.tv_password)).perform(typeText("123456"), closeSoftKeyboard());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withText("Your email or password was incorrect. Please try again."))
                .inRoot(withDecorView(not(activityTestRule.getActivity().getWindow().getDecorView())))
                .check(matches(isDisplayed()));
    }

When login successfully, will open new activity, show welcome message and show toast message.

Screenshot 2016-07-10 10.45.34

My suggestion is split it two tests. First, for check welcome message is display correctly.

    @Test
    public void loginSuccessfully_shouldShowWelcomeMessage() {
        onView(withId(R.id.tv_email)).perform(typeText("user@email.com"), closeSoftKeyboard());
        onView(withId(R.id.tv_password)).perform(typeText("123456"), closeSoftKeyboard());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withId(R.id.tv_welcome)).check(matches(withText("Hi user@email.com!")));
    }

Second, check toast message was shown up.

    @Test
    public void loginSuccessfully_shouldShowToast() {
        onView(withId(R.id.tv_email)).perform(typeText("user@email.com"), closeSoftKeyboard());
        onView(withId(R.id.tv_password)).perform(typeText("123456"), closeSoftKeyboard());
        onView(withId(R.id.btn_login)).perform(click());
        onView(withText("Login successfully."))
                .inRoot(withDecorView(not(activityTestRule.getActivity().getWindow().getDecorView())))
                .check(matches(isDisplayed()));
    }

Run test by click on green arrow in font of test class or execute following command.

./gradlew connectedAndroidTest

*Note we need to use emulator or device to run Expresso test.

Here the result.
Screenshot 2016-07-10 17.11.34

Source code: Github
for more information: https://google.github.io/android-testing-support-library/


Comments are closed.