Awesome
unit testing Android applications
by Wojtek Erbetowski
The road
Android test support
Unit tests - understanding the need
Long way to JVM
Shadowing Android with Robolectric
Raising the bar
Author
Community
Warsaw Java User Group
Mobile Warsaw
Warsjawa
Git Kata
Mobile Central Europe
Programming
Groovy, Scala, Python, Java
Team

Tech Lead @ Polidea
Android test framework
Getting started
public class MainActivityFunctionalTest extends
ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity activity;
public MainActivityFunctionalTest() {
super(MainActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
activity = getActivity();
}
public void testStartSecondActivity() throws Exception {
// add monitor to check for the second activity
ActivityMonitor monitor =
getInstrumentation().
addMonitor(SecondActivity.class.getName(), null, false);
// find button and click it
Button view = (Button) activity.findViewById(R.id.button1);
// TouchUtils handles the sync with the main thread internally
TouchUtils.clickView(this, view);
// to click on a click, e.g., in a listview
// listView.getChildAt(0);
// wait 2 seconds for the start of the activity
SecondActivity startedActivity = (SecondActivity) monitor
.waitForActivityWithTimeout(2000);
assertNotNull(startedActivity);
// search for the textView
TextView textView = (TextView) startedActivity.findViewById(R.id.resultText);
// check that the TextView is on the screen
ViewAsserts.assertOnScreen(startedActivity.getWindow().getDecorView(),
textView);
// validate the text on the TextView
assertEquals("Text incorrect", "Started", textView.getText().toString());
// press back and click again
this.sendKeys(KeyEvent.KEYCODE_BACK);
TouchUtils.clickView(this, view);
}
}
Robotium
public class EditorTest extends
ActivityInstrumentationTestCase2<EditorActivity> {
private Solo solo;
public EditorTest() {
super(EditorActivity.class);
}
public void setUp() throws Exception {
solo = new Solo(getInstrumentation(), getActivity());
}
public void testPreferenceIsSaved() throws Exception {
solo.sendKey(Solo.MENU);
solo.clickOnText("More");
solo.clickOnText("Preferences");
solo.clickOnText("Edit File Extensions");
Assert.assertTrue(solo.searchText("rtf"));
solo.clickOnText("txt");
solo.clearEditText(2);
solo.enterText(2, "robotium");
solo.clickOnButton("Save");
solo.goBack();
solo.clickOnText("Edit File Extensions");
Assert.assertTrue(solo.searchText("application/robotium"));
}
@Override
public void tearDown() throws Exception {
solo.finishOpenedActivities();
}
}
Project structure
MyProject/
AndroidManifest.xml
res/
... (resources for main application)
src/
... (source code for main application) ...
tests/
AndroidManifest.xml
res/
... (resources for tests)
src/
... (source code for tests)
NBS project structure
MyApplicationProject
└── MyApplication
└── src
├── instrumentTest
│ └── java
└── main
├── java
└── res
Running the tests
- compile
- dex
- package
- install
- run app
- x2
:-(
- Continuous Integration
- Parallelization
- Time consuming
- DalvikVM only (Java, no ASM)
Unit Testing Applications
- Check if the code works!
- Fast feedback loop
- Point the exact broken piece
- Stable and trusted
- Readable, short, expressive
The need
Fast and stable
How fast needed?
A better way
Why not JVM?
android.jar
|
\/
Stub!
Rewrite android.jar?
Classloaders FTW!
Meet Robolectric

Sample test
// Test class for MyActivity
@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {
@Test
public void clickingButton_shouldChangeResultsViewText() throws Exception {
Activity activity = Robolectric.buildActivity(MyActivity.class).create().get();
Button pressMeButton = (Button) activity.findViewById(R.id.press_me_button);
TextView results = (TextView) activity.findViewById(R.id.results_text_view);
pressMeButton.performClick();
String resultsText = results.getText().toString();
assertThat(resultsText, equalTo("Testing Android Rocks!"));
}
}
Shadows
@Implements(AlphaAnimation.class)
public class ShadowAlphaAnimation extends ShadowAnimation {
private float fromAlpha;
private float toAlpha;
public void __constructor__(float fromAlpha, float toAlpha) {
this.fromAlpha = fromAlpha;
this.toAlpha = toAlpha;
}
public float getFromAlpha() {
return fromAlpha;
}
public float getToAlpha() {
return toAlpha;
}
}
Not bad.
Awesome
- mocking static methods
- fast execution
- running on JVM
Spock framework
Enterprise ready!

Raising the bar
JUnit
@Test
void schouldAggregateSevesUser() {
// given
User user = new User();
// when
aggregate.store(user);
// then
assertEquals(
user,
aggregate.findOnly()
);
}
Spock
def 'aggregate should save user'() {
given:
def user = new User()
when:
aggregate.store user
then:
aggregate.findOnly() == user
}
Test parameters
JUnit
// ...
// then,
assertEquals(sum(3, 5), 8);
assertEquals(sum(1, 5), 6);
assertEquals(sum(4, 5), 9);
assertEquals(sum(5, 3), 8);
Spock
// ...
expect:
sum(a, b) == c
where:
a | b || c
3 | 5 || 8
1 | 5 || 6
4 | 5 || 9
5 | 3 || 8
Mocks
JUnit
// given
User userMock = mock(User.class);
when(userMock.getEmail())
.thenReturn("me@email.com")
.thenReturn(null);
// ...
// then
verify(userMock, times(2)).getEmail()
Spock
given:
def userMock = Mock(User)
userMock.getEmail() >> ['me@email.com', null]
// ...
then:
2 * userMock.getEmail()
Exception type
JUnit
@Test(expect=RuntimeException.class)
public void myTest() {
thisThrowsSomething();
}
Spock
when:
thisThrowsSomething()
then:
thrown(RuntimeException)
Exception details
JUnit
@Test
public void myTest() {
try {
thisThrowsSomething();
fail();
} catch(RuntimeException e) {
assertContains(
e.getMessage(), 'No such user')
}
}
Spock
when:
thisThrowsSomething()
then:
def e = thrown(RuntimeException)
e.message =~ 'No such user'
Groovyness
then:
userStorage.getAllUsers().find{it.id == id}?.name == "Simon"
Condition not satisfied:
userRepository.findAll().find{it.name == 'Simon'}?.age == 10
| | | | |
| | null | false
| [A$User(Adam, 12)] null
A$UserRepository@22d3d11f
<Click to see difference>
Hello RoboSpock
Robolectric
classloader, shadows, DB support , res
Spock
runner, extension points, everything else
Setup
.
├── app
│ ├── build.gradle
│ ├── debug.keystore
│ ├── local.properties
│ └── src
│ ├── instrumentTest
│ ├── main
│ └── release
├── robospock
│ ├── build.gradle
│ └── src
│ └── test
└── settings.gradle
Setup
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.+'
classpath 'com.novoda:gradle-android-test-plugin:0.9.1-SNAPSHOT'
}
}
apply plugin: 'groovy'
apply plugin: 'android-test'
repositories {
mavenCentral()
}
dependencies {
testCompile 'pl.polidea:RoboSpock:0.4'
testCompile rootProject
}
Setup
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.5.+'
}
}
apply plugin: 'android'
dependencies {
compile 'com.android.support:support-v4:13.0.0'
debugCompile 'com.android.support:support-v13:13.0.0'
compile 'com.google.android.gms:play-services:3.1.36'
}
android {
compileSdkVersion 15
buildToolsVersion "17.0"
testBuildType "debug"
signingConfigs {
myConfig {
storeFile file("debug.keystore")
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
defaultConfig {
versionCode 12
versionName "2.0"
minSdkVersion 16
targetSdkVersion 16
}
buildTypes {
debug {
packageNameSuffix ".debug"
signingConfig signingConfigs.myConfig
}
}
}
Are we there yet?
- Lower time consumption
- Local JVM
- Mocking dependencies
- Spock & Groovy
- Simple setup
- Better classloaders creation time
Examples
ORMLite
def "should throw SQL Constraint exception on existing primary key"() {
given:
def dao = databaseHelper.getDao(DatabaseObject)
and: 'stored object'
def dbObject = new DatabaseObject("test", 4, 1)
dao.create(dbObject)
when: 'duplication'
dao.create(dbObject)
then:
def exception = thrown(RuntimeException)
exception.message =~ "SQLITE_CONSTRAINT"
exception.cause.class == SQLException
}
RoboGuice
class TaskActivityTestGroovy extends RoboSpecification { @Inject WebInterface webInterface def setup() { inject { install(TestTaskExecutorModule) bind(WebInterface).toInstance(Mock(WebInterface)) } } def "should load text from async task"() { given: def taskActivity = new TaskActivity()
webInterface.getMainPageText() >> "WebText" when: taskActivity.onCreate(null) then: taskActivity.asyncText.text == "WebText" } }
Sources
Follow up
Home/blog: erbetowski.pl
Twitter: erbetowski
Awesome unit testing Android apps (Java2Days)
By Wojtek Erbetowski
Awesome unit testing Android apps (Java2Days)
- 3,082