Apr 25 2013

Testing the Android way

As a Rails developer, I learned the benefits of a test-driven style, and I never want to go back to the old style of writing code that might work, but might not! So when I decided to learn Android programming, finding the right strategies and tools to drive tests was the first task at hand. I found several excellent libraries that help facilitate a test-driven workflow on Android, and a test culture that is rapidly adopting the use of automated testing.

Robolectric

The largest barrier when getting started on this challenge was the dependency of the Android core libraries themselves upon the actual Android operating system. The AndroidTestCase classes provided by Google, for example, need a new instance of an emulator to run. This emulator then needs a new instance of the APK under test on it. The whole cycle of spinning up the emulator, deploying the APK and running the actual test could take a few minutes upon every run of the suite to get set up—a major time sink.

These slow tests were a dealbreaker, and I felt it would likely reduce the chances we’d actively use the tests. Fortunately for us, the Robolectric Project from Pivotal Labs has done the hard work of removing this dependency.

Effectively, Robolectric replaces the behavior of code that would otherwise require an emulator or actual device with its own, and once it’s set up, we can write jUnit-style tests against our classes without needing the baggage of the emulator. It does this using so-called “Shadow Classes,” mock implementations of Android core libraries.

Robolectric also goes further than this, enabling you to provide your own custom behavior for these shadow classes. This is extremely helpful for custom test behavior you may need. For example, my current project depends upon loading large sets of data to the SQLite database managed by Android. Using the “Shadow Class” notion, Robolectric allowed me to implement a ‘test fixture’ style database reset, where any changes that may have happened to a default set of data I provide gets reset to ensure that I can assert results dependent upon this data. Here’s what I mean:

public class MyTestRunner extends RobolectricTestRunner{
  @Override
  public void beforeTest(Method method) {
    super.beforeTest(method);
    // swaps in custom implementations of the sqlite android database class.
    Robolectric.bindShadowClass(MyShadowSQLiteDatabase.class);
  }
}

The MyShadowSQLiteDatabase implementation provides custom behavior that points to our custom test database. Without Robolectric, we would be dependent upon the core Android class behavior. Within the ShadowSQLiteDatabase class, we now change how Android’s core behavior would normally work:

@Implements(SQLiteDatabase.class) //this annotation indicates to android what implementation this class is providing
public class MyShadowSQLiteDatabase extends ShadowSQLiteDatabase{
    @Implementation
    public static SQLiteDatabase openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags){
        try {
            //Replace Robolectric's in-memory only connection with a real sqlite database connection.
            connection = DriverManager.getConnection("jdbc:sqlite:" + path); 
        } catch (SQLException e) {
            e.printStackTrace();
        } 
        return newInstanceOf(SQLiteDatabase.class); //an instance of the sqlite-jdbc sqliitedatabase class
    }

}

And then in the test itself we can finally use this new behavior:

@RunWith(MyTestRunner.class)
public class TestDatabaseStuff extends BaseTest{

  public static final String TEST_DATABASE = "test/fixtures/test_database.s3db";
  public static final String ORIG_DATABASE = "test/fixtures/test_database.orig";

  @Before
  public void setup() throws IOException{
    //reset the test fixture
    originalDatabase = new File(ORIG_DATABASE);
    fileToWrite = new File(TEST_DATABASE);
    FileUtils.copyFile(originalDatabase, fileToWrite); //resets the test database with a new copy
  }
}

Syntactic Sugar

I had a way to run tests quickly, but I then began to look for ways to write tests faster and more cleanly.

Fest for Android

The first discovery was Fest-Android, a library extension for the FEST framework from the fine folks at Square Labs. Fest-Android is a great improvement upon the regular jUnit-style assertions. It gives a chainable (or “fluent”) syntax for checking assertions, and makes tests easier to write (and read!). A bonus of this is that any decent Java IDE can code-complete the available assertions for any property, cleaning up the confusion with the “expected” and “actual” syntax in the jUnit-style test syntax.

For example:

//regular junit:
assertEquals(View.GONE, view.getVisibility());
//fest!
assertThat(view).isGone();

At the point assertThat(view) is called, the IDE can then give intelligent feedback about the types of assertions available.

Mockito = rspec-mocks… Sort of

For more complicated classes, replacing elaborate functionality coupled to other classes in order to isolate tests is done using the notion of ‘mocks’. The Mockito framework for Java does this well. An example from a project was to selectively change the behavior of a method to return a timestamp I could test against, rather than a dynamically generated one. Mockito made this a snap:

Mockito.doReturn((long) 1363027600).when(myQueryObject).getCurrentTime();

Whenever myQueryObject.getCurrentTime is called, a predefined value is returned instead of the current one. This is very useful for having classes return results you can actually test!

Robotium = Integration Tests

Sometimes, unit tests alone fail to address behavior you will want to check with your tests. For example, let’s say I want to assert that entering a valid username and password, clicking the login button, and then clicking on the account details button takes me to the right view within my application. A unit test doesn’t really capture this “path” through the application, and is better described using an “integration test,” or a test that checks whether multiple components of the system work properly in conjunction with one another. For this type of work, I found Robotium, which uses a Selenium-like style to run the test from the UI. This of course requires an emulator or device to work. I found that keeping the unit test projects and integration test projects separate was a good compromise between the fast Robolectric tests, and the sometimes-necessary Robotium tests to check overall behavior across the system.

Dependency Injection

To reduce the amount of effort required to set up classes for tests, the use of dependency injection is a huge win. Simply put, it allows you to configure the instantiation of classes across your application without manually doing so. It also allows the configuration of special behavior for classes without writing code from scratch. Roboguice brings the Google GUICE injection framework to Android, and makes it possible to use dependency injection throughout your Android app. Check out the Roboguice wiki on how to use this great tool.

Wrapping up

While test-driven development isn’t as common in the Android community as it is with Rails, it seems that doing things the TDD way is gaining momentum. I hope this post helped to give a good overview of some of the tools that will help you test your Android project. Don’t hesitate to ask any questions, or point out any other tools I should know about below.

10 Comments

  1. Steve

    Thanks for this. I’m also getting started with Android, coming from the Rails world. This blog post is the best of its kind that I’ve seen to date, and is a nice validation of my team’s decision to go with Robolectric + Robotium. Also I hadn’t heard of the mocking & syntactic sugar libraries you mention and I’m looking forward to pulling them into our project.

    I’m curious if you’ve had experience or have opinions about a couple of other things which I’m still mulling over.

    Specifically:
    1. ORMs. So far the most interesting option I’ve seen is ActiveAndroid (it has a stated goal of exposing an ActiveRecord-like API which is of particular interest to me).
    2. REST clients. It appears that the most popular option is loopj’s android-async-http library, which implements Runnable to achieve asynchronous callback-based http. What I can’t quite figure out is how this jives with the Google-recommended way of doing REST (which is by extended IntentService).

    Your thoughts would be greatly appreciated.

  2. Dave Shah

    Great post. Our team has a strong TDD background that nearly came to a grinding hault when jumping over to Android 2 years ago. We’ve learned a lot along the way and have come to embrace many of the same tools you mention in this post. For those coming from a Ruby background, familiar with Cucumber & interested in ATDD with Android, you might want to checkout Calabash-Android or our homegrown Gametel ruby gem.

  3. Leena

    Great post. Thanks for writing. FEST looks very useful for guys who prefer RSpec style over JUnit style.

  4. Leena

    Hi Josh,

    Wondering whether its possible for you to actually share some sample test code for Roboelectric? I am finding it hard to get started with the same. Will you be able to help?

    Thanks,
    Leena

  5. Hi Leena,
    That is a great idea and something we have talked about internally to add – while we don’t have something like that available yet we are working on getting examples together we can share.
    In the meantime, you could take a look at https://github.com/robolectric/RobolectricSample – it’s a pretty good starting place for how to structure your tests (i used this to get a handle on how to structure my integration tests).
    Josh

  6. Simon Coffey

    Thanks for this, this is really helpful stuff. Really happy to learn about Robotium; for a while there the official docs really had me thinking that monkeyrunner + image comparison was state of the art for Android acceptance testing. :-)

    Cheers,
    SImon

  7. Ian

    Hi Josh thanks for this tutorial. It has been hugely helpful to me.

    One thing I’m still completely confused about though is the implemenation of MyShadowSQLiteDatabase. In the above example it extends ShadowSQLiteDatabase and the variable “connection” is used. This is a private static variable of ShadowSQLiteDatabase. How are you accessing it? Something does not seem right.

    The only way I can see this possibly working is to copy all the code for ShadowSQLiteDatabase and to just change the implementation of the openDatabase() method as you showed above. Is that the right way?

    Take care,
    Ian

  8. Darien Alvarez

    Hello:

    Can you update this post using roboelectric 2.2

    Actually y use roboelectric, roboguice and ormlite …. i can not get the helper instance

    while locating com.devsu.supermaxi.data.repository.impl.ItemsRepositoryImpl
    at com.devsu.supermaxi.data.repository.guice.RepositoryModule.configure(RepositoryModule.java:35)
    while locating com.devsu.supermaxi.data.repository.ItemsRepository
    Caused by: java.lang.IllegalStateException: Could not construct instance of helper class class com.devsu.supermaxi.data.DatabaseHelperImpl
    at com.j256.ormlite.android.apptools.OpenHelperManager.constructHelper(OpenHelperManager.java:222)
    at com.j256.ormlite.android.apptools.OpenHelperManager.loadHelper(OpenHelperManager.java:170)

  9. 4nh7i3m

    @LEENA: I found here a pretty detailed post about configuring the test environment with some examples. I hope it’ll help you
    http://hintdesk.com/android-basic-testing-with-unit-test-android-test-and-roboguice-robolectric-mockito/

Leave a Comment

Join the discussion. Do not worry, your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>