In this tutorial, we’ll be discussing Espresso Testing Framework for Testing the UI of Android Applications. We’ll be creating a basic login application with a few espresso tests.
Table of Contents
Android Espresso
We’ve already discussed Android Unit Testing before.
Testing is an important area for building any product.
It helps us in:
- Detecting bugs/cases not covered by our code.
- Keep manual user testing to minimal. No need to test the earlier features again and again manually.
- Building robust applications with end cases covered.
Testing in Android majorly involves two types:
- Unit Tests
- Instrumentation Tests
Espresso comes under the second type. It is used to do automated UI testing by writing short and concise Android UI tests.
Let’s look at the components of the Espresso Instrumentation Testing Framework.
ViewMatchers, ViewActions, ViewAssertions
- ViewMatchers – allows us to find a view in the view hierarchy.
- ViewActions – allows us to perform automated actions on the view. Such as clicks etc.
- ViewAssertions – allows us to assert the state of the view.
A basic skeleton code of an Espresso Test is:
onView(ViewMatcher)
.perform(ViewAction)
.check(ViewAssertion)
Inside the onView
we look for the id of the View.
It can be done using methods like:
withId()
– Pass the unique idwithText()
– Pass the text of the view. It searches for a view with the specified text.
Inside perform
we pass the action to be done on the View. Example:
click()
– Clicks the view passed in onView.typeText()
– Pass the string to be entered in the View. This is especially used in EditText.replaceText()
– Replaces the current text with the string that’s passed.closeSoftKeyboard()
– Dismisses the keyboard.
Inside check
we assert the states mainly using methods:
matches
doesNotExist
For example we can Position View Assertions in the following ways:
onView(withId(R.id.textView)).check(isRightOf(withText("Hello World")));
onView(withId(R.id.textView)).check(isBelow(withText("Hello World")));
Other methods for position assertions are:
isLeftAlignedWith
isAbove
isTopAlignedWith
The following examples show that:
onView(withText(startsWith("Hello"))).perform(click());
onView(allOf(withId(R.id.textView),isDisplayed()));
onView(allOf(withId(R.id.textView),hasLinks()));
The last one checks whether the whole TextView is a link.
In the following section, we’ll create an android application and write our first espresso tests.
Our android application would contain a basic login form with a username, phone number and confirm number fields. On pressing the Login Button, if the phone number fields don’t match we will show a Toast message.
If everything is fine, we’ll great the user with a Hello.
Getting Started With Espresso Tests
Add the following dependencies in the build.gradle file:
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
Add the following in the defaultConfig block :
android{
...
defaultConfig{
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
...
}
AndroidJUnitRunner is the instrumentation runner. It is the entry point into running the Android Tests.
Following is how our app’s build.gradle
file looks :
Make sure you disable the Animations in your Android device before running Espresso tests.
Otherwise, the default screen animations may interfere with the tests. You can disable them from the Developer Options as shown below:
Project Structure
Code
The code for the activity_main.xml
layout is given below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="8dp"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/inUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Username" />
<EditText
android:id="@+id/inNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Number"
android:inputType="number" />
<EditText
android:id="@+id/inConfirmNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Confirm Number"
android:inputType="number" />
<Button
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="LOGIN" />
<TextView
android:id="@+id/txtLoginResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center" />
</LinearLayout>
The code for the MainActivity.java class is given below:
package com.journaldev.androidexpressobasics;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
EditText inUsername, inNumber, inConfirmNumber;
Button btnLogin;
TextView txtLoginResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inUsername = findViewById(R.id.inUsername);
inNumber = findViewById(R.id.inNumber);
inConfirmNumber = findViewById(R.id.inConfirmNumber);
btnLogin = findViewById(R.id.btnLogin);
txtLoginResult = findViewById(R.id.txtLoginResult);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (TextUtils.isEmpty(inNumber.getText()))
Toast.makeText(getApplicationContext(), R.string.number_empty, Toast.LENGTH_SHORT).show();
else if (!(inNumber.getText().toString().equals(inConfirmNumber.getText().toString())))
Toast.makeText(getApplicationContext(), R.string.toast_error, Toast.LENGTH_SHORT).show();
else if (inUsername.getText().toString().trim().length() == 0)
Toast.makeText(getApplicationContext(), R.string.username_empty, Toast.LENGTH_SHORT).show();
else
txtLoginResult.setText("Hello " + inUsername.getText().toString().trim());
}
});
}
}
The code for the strings.xml file is given below:
Writing Espresso Tests
Espresso tests are written inside src | androidTest folder files.
By default, ExampleInstrumentedTest.java
is created with a default test.
Let’s write one test inside it:
package com.journaldev.androidexpressobasics;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
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.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.support.test.espresso.Espresso.onView;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.*;
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
public static final String USERNAME_TYPED = "Anupam";
public static final String LOGIN_TEXT = "Hello Anupam";
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.journaldev.androidexpressobasics", appContext.getPackageName());
}
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
MainActivity.class);
@Test
public void loginClickedSuccess() {
onView(withId(R.id.inUsername))
.perform(typeText(USERNAME_TYPED));
onView(withId(R.id.inNumber))
.perform(typeText("12345"));
onView(withId(R.id.inConfirmNumber))
.perform(typeText("12345"));
onView(withId(R.id.btnLogin)).perform(click());
onView(withId(R.id.txtLoginResult)).check(matches(withText(LOGIN_TEXT)));
}
}
@Rule
annotation is used to launch the MainActivity.
Inside loginClickedSuccess
we wrote a successful login test case.
Now let’s write two more tests in which we will test whether the Toast is shown correctly or not.
Add the following methods inside the above class:
@Test
public void shouldShowToastError() {
onView(withId(R.id.inUsername))
.perform(typeText(USERNAME_TYPED));
onView(withId(R.id.inNumber))
.perform(typeText("123456"));
onView(withId(R.id.inConfirmNumber))
.perform(typeText("12345"), closeSoftKeyboard());
onView(withId(R.id.btnLogin)).perform(click());
onView(withText(R.string.toast_error)).inRoot(withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView())))).check(matches(isDisplayed()));
}
@Test
public void shouldShowToastUsernameEmpty() {
onView(withId(R.id.inNumber))
.perform(typeText("12345"));
onView(withId(R.id.inConfirmNumber))
.perform(typeText("12345"));
onView(withId(R.id.btnLogin)).perform(click());
onView(withText(R.string.username_empty)).inRoot(withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView())))).check(matches(isDisplayed()));
}
The last line is used to check whether Toast is displayed with the correct string or not.
To run the espresso test. Right click on the ExampleInstrumentationTest.java and press run to start the automated test on the emulator.
Following is the output of the application in action:
Following is the output in the console while the above automated UI test was running:
Espresso Test Recording
You can record Espresso tests manually as well:
It will automatically create a new file with the espresso tests containing the manual clicks and actions you did in the device.
This brings an end to this tutorial. You can download the project from the link below:
how to write test case for selectors