Android Unit Testing Part II: Escaping Dalvik's Hold
This is the second of a four part series on Android Unit Testing. In these posts, we’ll walk through the key steps engineers should take to make Android test fast by running them on JVM (versus running them on emulator).
For background information on the importance of Android testing, visit Part I of the series.
It appears that the need to run tests on an Android device or an emulator has concerned Android engineers for almost as long as Android has existed – and Christian Williams created Robolectric to solve this problem. Robolectric allows you to run unmodified test code (referring to Android specific classes) on your desktop (in a JVM) instead of running them on an emulator or device in the Android Virtual Machine, or Dalvik.
I have listed several good tutorials at the end of this post that illustrate exactly how this can be done, but they also include some details you may not yet need. So, use the tutorial links for details, but check out “My Summary” for a short overview of what you need to do:
My Summary
-
Create a new project in Android Studio (I used Studio 0.8.14)
Choose as example Blank Activity project.
-
Modify the TOP Gradle file
Add the following code to the dependencies section:
classpath 'org.robolectric:robolectric-gradle-plugin:0.12.+'
-
Modify the application Gradle file
Add the following code under
apply plugin: 'com.android.application'
:apply plugin: 'robolectric'
-
Add the following under the dependencies section:
androidTestCompile('junit:junit:4.11') androidTestCompile('org.robolectric:robolectric:2.3')
-
Add this section:
robolectric { include '**/*Test.class' }
-
Create the code that you want to test
Modify
MainActivity
. Add the following code to it:private Foo foo = new Foo(); public int getSomething() { return foo.getFoo(); }
Add the
Foo
class:package com.example.myapplication; public class Foo { Bar bar = new Bar(); public int getFoo() { return bar.getBar(); } }
-
Add the
Bar
class:package com.example.myapplication; public class Bar { public int getBar() { return 4; } }
-
Create a test
Delete the
ApplicationTest
file.Create the following
FooTest
class under yourandroidTest
:package com.example.myapplication; import junit.framework.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class FooTest { Foo sut; @Before public void setUp() { sut = new Foo(); } @Test public void testGetFoo_returns4() { // Arrange // Act int actualResult = sut.getFoo(); // Assert Assert.assertEquals(4, actualResult); } }
-
Create the configuration
- Create a gradle configuration.
- Set “Tests” as a name.
- Choose the top gradle file as a project.
- Type test in Tasks.
Now, without launching the emulator, you can run this configuration and see that your test has passed. It is much faster than before—and repeatable. You can put this under build automation and it will totally work.
-
JVM
There are alternative ways to run the test on a JVM. For example, you can create a JUnit task and ensure that all your tests and classes don’t touch any Android specific classes. However, this is not easy, as you must design all your code with this restriction in mind.
The changes which we did to run on JVM are great, but we are still facing the limitations of using integration tests. For example, if the implementation of a
Bar
class changes and now uses the network, you might start seeing flakiness in thetestGetFoo_returns4
test because of a bad network connection.
Additional Resources
- Robolectric Installation for Unit Testing
- Android Testing With Robolectric
- Robolectric Gradle Plugin
Stay tuned for part three of our series, where I will show you how to achieve test isolation using dependency injection. You can also check out the full code at GitHub.
Okta Developer Blog Comment Policy
We welcome relevant and respectful comments. Off-topic comments may be removed.