Ian Munene
Instructor/Software developer @moringaschool
Twitter: @ianthe_ninja
Github: iantheninja
Android UI Testing
Android applications run on devices with limited memory, CPU power, and power supply
Android UI Testing
Therefore, they can always be negatively impacted by external factors like connectivity, general system utilization, etc.
Android UI Testing
We'll use Robolectric unit testing framework to test our app locally
Android UI Testing
Let’s start with a simple test using our MyRestaurants app
To get started, configure your app to use the 'org.apache.http.legacy' library
build.gradle(Module: app)
Android UI Testing
android {
...
useLibrary 'org.apache.http.legacy'
...
repositories {
mavenCentral()
jcenter()
}
...
}
dependencies {
...
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.robolectric:shadows-support-v4:3.0'
}
Android UI Testing
Navigate to java/com.yourpackagename.myrestaurants(test), right click on the test package name (example highlighted above), and create a new class called MainActivityTest:
com.ianmunene.myrestaurants(test)/MainActivityTest.java
Android UI Testing
Add the above annotation to our test class to allow our code to be run natively in the JVM instead of on an Android device
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
}
com.ianmunene.myrestaurants(test)/MainActivityTest.java
Android UI Testing
Remember, each time a class name appears red, it means the class needs to be imported. You can do this by clicking on the red and underlined class name, and pressing Alt + Enter. After importing all classes, your MainActivityTest file should look like this:
com.ianmunene.myrestaurants(test)/MainActivityTest.java
Android UI Testing
package com.ianmunene.myrestaurants;
import android.os.Build;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
}
com.ianmunene.myrestaurants(test)/MainActivityTest.java
Android UI Testing
Before we write our first test, we'll also want to configure our test class so it knows which Activity we will use to write our tests:
com.ianmunene.myrestaurants(test)/MainActivityTest.java
Android UI Testing
package com.ianmunene.myrestaurants;
import android.os.Build;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
private MainActivity activity;
@Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class);
}
}
Android UI Testing
For our first test, we will assert that the text in our MainActivity's TextView is equal to “MyRestaurants”.
Android UI Testing
We are finally ready to write our first test:
...
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
private MainActivity activity;
@Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class);
}
@Test
public void validateTextViewContent() {
TextView appNameTextView = (TextView) activity.findViewById(R.id.appNameTextView);
assertTrue("MyRestaurants".equals(appNameTextView.getText().toString()));
}
}
Android UI Testing
Android UI Testing
Right click on the test file and select Run 'MainActivityTest'
your test should fail without the test view set to "MyRestaurants" in your main activity or pass if it has been set.
Android UI Testing
Next, let’s learn the basics of instrumentation testing in Android. We will use Espresso; a UI test framework that creates automated tests that run on an actual device or emulator. Instrumentation tests are meant to simulate the actions of a user and allow us to test the app through the stages of the android activity lifecycle.
Android UI Testing
First, we’ll add the necessary configurations and dependencies to our build.gradle file:
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
...
}
dependencies {
...
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestCompile('com.android.support.test:runner:0.3') {
exclude group: 'com.android.support', module: 'support-annotations'
}
}
Android UI Testing
Next, let’s create a new test class called MainActivityInstrumentationTest inside our java/com.ianmunene.myrestaurants (androidTest) package.
NB: please check to confirm you are creating it in the androidTest package
We'll place the following annotation within this file, and import all necessary classes.
MainActivityInstrumentationTest.java
Android UI Testing
package com.ianmunene.myrestaurants;
import android.support.test.rule.ActivityTestRule;
import org.junit.Rule;
public class MainActivityInstrumentationTest {
@Rule
public ActivityTestRule<MainActivity> activityTestRule =
new ActivityTestRule<>(MainActivity.class);
}
The code in @Rule tells our device which activity to launch before each test. Here, we're instructing the instrumentation tests to launch the MainActivity before each test.
Android UI Testing
Now we are ready to write our first test. Include the @Test block detailed below. Multiple methods will appear red. This is normal, continue following to the next section to address importing these methods appropriately.
MainActivityInstrumentationTest.java
Android UI Testing
public class MainActivityInstrumentationTest {
...
@Test
public void validateEditText() {
onView(withId(R.id.locationEditText)).perform(typeText("Nairobi"))
.check(matches(withText("Nairobi")));
}
}
Android UI Testing
You will notice that multiple methods included in this block of code will be red. Each of these are static methods from Espresso that need to be imported, similar to the manner in which we import classes. Click on each, select Alt + Enter, and select "import static method..." from the resulting menu.
Once complete, your file should have these imports and annotations:
Android UI Testing
...
import android.support.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
import static android.support.test.espresso.Espresso.onView;
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;
public class MainActivityInstrumentationTest {
@Rule
public ActivityTestRule<MainActivity> activityTestRule =
new ActivityTestRule<>(MainActivity.class);
@Test
public void validateEditText() {
onView(withId(R.id.locationEditText)).perform(typeText("Portland"))
.check(matches(withText("Portland")));
}
}
Android UI Testing
Android UI Testing
Let’s run our test and make sure it passes. Right click on the class name and select Run ‘MainActivityInstrume…’. Because this is an instrumentation test, the emulator will open, the “Nairobi” text will appear in the locationEditText, and the progress indicator will notify us that our test passed. Hooray!
Android UI Testing
ian@moringaschool.com
santaclaus@moringaschool.com