[Gradle] Running unit tests without using emulator/connected device

859 views
Skip to first unread message

Per Christian Henden

unread,
Jun 12, 2014, 4:38:17 AM6/12/14
to adt...@googlegroups.com
Hi!

I came up with a way to run unit tests on the local machine, without running the emulator. 
It relies on mocking (Mockito) to deal with the classes from the Android-api. 
Initially I used Robolectric, but I ran into some issues with variant support. 
Also, I suspect the normal androidTests doesn't run when using Robolectric (either vanilla or Robolectric runs). 

Some parts are hackish and I would like your suggestions on how to make it cleaner!
Especially I'm interested in how to make the test folders register with Android Studio. 

Here is my code, for plugin version 0.11, tested with Gradle 1.12. 

I execute the tests by 'gradle unitTest' and the output is put into build/reports/tests and build/test-results like normally in Gradle. 

sourceSets {

    test {

        java.srcDir file('src/test/java')

        resources.srcDir file('src/test/resources')


        // Add test-dependencies to classpath

        compileClasspath += configurations.testCompile


        // Add our classes to classpath

        compileClasspath += files("${buildDir}/intermediates/classes/plain/debug")

        runtimeClasspath = output + compileClasspath

    }

}


task unitTest(type: Test) {

    testClassesDir = sourceSets.test.output.classesDir

    classpath = sourceSets.test.runtimeClasspath

}

check.dependsOn unitTest

android.libraryVariants.all {

    if (it.name.equals("plainDebug")) {

        testClasses.dependsOn(it.javaCompile)

    }

}


dependencies {

    // compile time dependencies are left out in this example

    testCompile('junit:junit:4.11')

    testCompile 'com.squareup:fest-android:1.0.8'

    testCompile 'org.mockito:mockito-core:1.9.5'

    testCompile files(plugins.findPlugin('android-library').getBootClasspath()) // android jar

}

Jürgen Cruz

unread,
Jun 12, 2014, 8:16:14 AM6/12/14
to adt...@googlegroups.com
As far as I know, even with mockito, you can't run android clases in JVM. That is why robolectric had to make a runner that intercepts the bytecode and a lot more magic things.

But you are having the same problem as robolectric users. The best I have been able to do was to manually modify the .iml files to include the source and the libraries folder to include the dependencies

Per Christian Henden

unread,
Jun 12, 2014, 8:54:21 AM6/12/14
to adt...@googlegroups.com
Yes, you don't get the real Android classes with Mockito, you get mock implementations, which is a nice solution for unit tests. 
Basically you specify through Mockito how each Android class that your test code depends on should behave. Typically this means instrumenting the Android classes to return some static dummy values so that your test code can do its work. It's not a good fit if you are testing things related to the GUI or Activity lifecycle. 

Thanks for pointing out that the workarounds suggested for Android Studio/IDEA for Robolectric-gradle applies to this case too :)

Jürgen Cruz

unread,
Jun 12, 2014, 10:17:37 AM6/12/14
to adt...@googlegroups.com
Yes, the android classes mocked by mockito should work, except for final classes (Bundle) or final methods. But as you said, testing a class that inherits from an Android class (Activity, Service, BroadcastReceiver) will not work since you can't mock them and you aren't running on emulator. But everything else should work. And yeah you can use a robolectric gradle plugin and just make your tests not use robolectric at all and you won't have to reinvent the wheel.

Jürgen

Per Christian Henden

unread,
Jun 12, 2014, 10:39:25 AM6/12/14
to adt...@googlegroups.com
Yes, subclassing android-classes may be a relevant use-case for some. 
I just need to test my non-android-specific Java code in an efficient manner. 

If Robolectric works for you, i.e. you are not using build variants and don't need to run additional on-device androidTests, then yes, of course, you wouldn't need anything else. 

Come to think of it, if you manage to keep the relevant classes completely android-free, you could build and test that code in a separate Gradle-project and depend on the jar it produces. This is hard in my experience, as you often need Context and Log.

Andreas

unread,
Jun 14, 2014, 3:43:29 AM6/14/14
to adt...@googlegroups.com
Hi Per.

I have Robolectric tests running fine with build variants using the Robolectric Gradle test plugin. The test plugin exposes tasks to execute flavor specific test (with non-release build types).
I just had to do two things to get it to work,
1. Register new test source folders - android.sourceSets.androidTestX.setRoot('src/testX'), where X is your flavor
2. Insert a hack to the "testX" task exposed by the test plugin to avoid running tests compiled for a previously run flavor in the current flavor - This may be a bug in the plugin or just in my build scripts, but I essentially had to add a testX.doFirst where I delete any test classes belonging to other flavors from build/test-classes.

Cheers
Reply all
Reply to author
Forward
0 new messages