Android Unit Testing with Robolectric

    I have been doing a lot of Android programming of late. One of the tasks that I originally had was to get a continuous integration environment going so that I could quickly and efficiently test and integrate my new code with my existing codebase. In this post, I’ll talk about how I set up Robolectric using Gradle.

    Why Robolectric?

    If you’ve done any kind of Android development, you probably already know part of the answer to that question. As it turns out, testing on Android can be a little bit of a pain in the rear. While Google provides a really nice SDK, all of the functions in the android.jar file are stubbed out. This means when you try to unit test anything that makes a call against it, you get the wonderful Stub! error.

    To be fair, Google has provided a bunch of utilities and classes to help make unit testing possible. But, at the end of the day, you usually have to resort to pushing your test code onto an emulator (slow for a TDD approach), or an actual device (painful if you have a CI environment that doesn’t have access to one). You can try to get around this by Mocking the Android objects you need, but that turns into a lot of work.

    Robolectric helps by providing a mock for you, allowing you to run your tests on a normal JVM instead of having to push them to the emulator or an actual device. Plus, it plays nicely with the Gradle lifecycle, allowing you to do things like ./gradlew clean test when you want to run your unit tests. Plus you can use JUnit 4 to test your vanilla Java classes.

    Setting it Up

    When I was trying to set up my environment, I ran into quite a number of articles that tried to explain how to configure your tests, and how to add the required statements to your build.gradle file. A lot of them were outdated – the Android development platform evolves rapidly. Keep that in mind when you read this – these instructions may no longer apply if it’s been even a month or two since this post was written! In the end, the instructions for the robolectric-gradle-plugin on their GitHub repository got me most of the way there, with a few tweaks to deal with certain problems (more below).

    The first thing that I did was add the required statements to my build.gradle file. First, I added the required buildscript parameters:

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'org.robolectric:robolectric-gradle-plugin:0.13.1'
        }
    }

    This just tells Gradle to use Maven Central repository when trying to get dependencies for the build script itself (project dependencies are declared in another block). I then applied the plugin:

    apply plugin: 'robolectric'

    Finally, I added the required androidTestCompile dependencies:

    dependencies {
        repositories {
            mavenCentral()
        }
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile 'org.robolectric:robolectric:2.3'
        androidTestCompile 'junit:junit:4.11'
    }

    Notice I also added the jUnit dependencies – this is because I have some vanilla classes I want to test as well using JUnit 4. Essentially, this just tells Gradle that the androidTest lifecycle target requires Robolectric version 2.3, as well as JUnit 4.11.

    This is all that is really required to get Robolectric up and running. I ran a sync in Android Studio…  and got a nice big error:

    Error:(11, 0) Cause: org/robolectric/gradle/RobolectricPlugin : Unsupported major.minor version 52.0

    A New Problem

    The Unsupported major.minor error was obvious: 52 is the version number for Java 8. I was using a Java 7 SDK, which has a major.minor version of 51. Looks like the Robolectric Gradle plugin on Maven Central was built with Java 8.

    While I could update my Java SDK to Java 8, I was worried that it wouldn’t play nice with my existing codebase. Instead, I figured it would probably be easier to download and compile the Robolectric Gradle plugin using my version of Java, and then include it as a static import in my code. This has some benefits – if I check the Robolectric Gradle plugin into source code control, then I’m always guaranteed of having a good version number without having to rely on automated imports. So, away I went.

    First, I cloned the Git repo:

    git clone https://github.com/robolectric/robolectric-gradle-plugin.git

    Second, I built the package. This was pretty simple since it uses a Gradle wrapper:

    cd robolectric-gradle-plugin
    ./gradlew build

    This generated the plugin’s jar files (including sources and javadocs) and put them in the following directory:

    build/libs

    Third, I copied all the jars into my application’s libs directory. Here is another little tip – for Gradle to pick up the jars, they needs fall under a certain directory structure. In this case, the compiled jar files needed to be copied to:

    libs/org/robolectric/robolectric-gradle-plugin/0.13.1/

    Fourth, and finally, I modified my buildscript portion of build.gradle to use a local Maven import from the local libs directory, instead of using Maven Central:

    buildscript {
        repositories {
            maven {
                url "./libs"
            }
        }
        dependencies {
            classpath 'org.robolectric:robolectric-gradle-plugin:0.13.1'
        }
    }

    I synced the settings file once again in Android Studio and it worked without errors.

    Creating Unit Tests

    Next, I tried creating unit tests (I actually already had a bunch of them, so I was already good to go). According to the README on the GitHub repository for the Robolectric Gradle plugin, I put my tests under the src/androidTest/java directory. To test it out, I created a unit test that I knew would fail, called FailTest.java:

    package ca.craigthomas.sampleproject.models;
     
    import org.junit.Test;
     
    import static org.junit.Assert.assertTrue;
     
    public class FailTest {
        @Test
        public void thisTestShouldFail() {
            assertTrue(false);
        }
    }

    I then got Gradle to run the unit tests (fingers crossed that it would work):

    ./gradlew test

    And got the following output:

    ca.craigthomas.sampleproject.models.FailTest > thisTestShouldFail FAILED
        java.lang.AssertionError at FailTest.java:10
     
    82 tests completed, 1 failed                                  
    :app:testDebug FAILED

    Success! As you can see, it also ran the other unit tests that I already had. If you want to see a nice report of your unit tests (say if you don’t have a CI environment that does this for you automagically), an HTML report will be generated at:

    build/test-report/index.html

    Wrapping Up

    In this blog post, I discussed the Robolectric Gradle plugin, and demonstrated how to integrate it into a simple Android project. In a future post, I’ll describe how to set up a CI environment to use the Gradle wrapper and the Android SDK to run unit tests automatically.

      Leave a Reply

      Your email address will not be published.


      *