Intro
Note that at the time of writing robolectric 2.4 is the latest version and has no support for appcompat v7
library. Support wil be added in robolectric 3.0 release (no ETA yet). Also ActionBar Sherlock
can cause issues with robolectric.
To use Robolectric within Android Studio you have 2 options:
(Option 1) - Running JUnit tests with Android Studio using a Java module
This technique uses a java module for all your tests with a dependency to your android module and a custom test runner with some magic:
Instructions can be found here: http://blog.blundellapps.com/how-to-run-robolectric-junit-tests-in-android-studio/
Also check the link at the end of that post for running the tests from android studio.
(Option 2) - Running JUnit Tests with Android Studio using robolectric-gradle-plugin
I encountered a few issues setting up junit tests to run from gradle in Android Studio.
This is a very basic sample project for running junit tests from a gradle based project in Android Studio: https://github.com/hanscappelle/android-studio-junit-robolectric This was tested with Android Studio 0.8.14, JUnit 4.10, robolectric gradle plugin 0.13+ and robolectric 2.3
Buildscript (project/build.gradle)
The build script is the build.gradle file in the root of you project. There I had to add robolectric gradle plugin to classpath
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'org.robolectric:robolectric-gradle-plugin:0.13.+'
}
}
allprojects {
repositories {
jcenter()
}
}
Project buildscript (App/build.gradle)
In the build script of your app module use the robolectric
plugin, add robolectric
config and add androidTestCompile
dependencies.
apply plugin: 'com.android.application'
apply plugin: 'robolectric'
android {
// like any other project
}
robolectric {
// configure the set of classes for JUnit tests
include '**/*Test.class'
exclude '**/espresso/**/*.class'
// configure max heap size of the test JVM
maxHeapSize = '2048m'
// configure the test JVM arguments
jvmArgs '-XX:MaxPermSize=512m', '-XX:-UseSplitVerifier'
// configure whether failing tests should fail the build
ignoreFailures true
// use afterTest to listen to the test execution results
afterTest { descriptor, result ->
println "Executing test for {$descriptor.name} with result: ${result.resultType}"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile 'org.robolectric:robolectric:2.3'
androidTestCompile 'junit:junit:4.10'
}
Create JUnit Test Classes
Now put the test classes in the default location (or update gradle config)
app/src/androidTest/java
And name your tests classes ending with Test (or again update config), extending junit.framework.TestCase
and annotate test methods with @Test
.
package be.hcpl.android.mytestedapplication;
import junit.framework.TestCase;
import org.junit.Test;
public class MainActivityTest extends TestCase {
@Test
public void testThatSucceeds(){
// all OK
assert true;
}
@Test
public void testThatFails(){
// all NOK
assert false;
}
}
Execute Tests
Next execute the tests using gradlew from command line (make it executable using chmod +x
if needed)
./gradlew clean test
Sample output:
Executing test for {testThatSucceeds} with result: SUCCESS
Executing test for {testThatFails} with result: FAILURE
android.hcpl.be.mytestedapplication.MainActivityTest > testThatFails FAILED
java.lang.AssertionError at MainActivityTest.java:21
2 tests completed, 1 failed
There were failing tests. See the report at: file:///Users/hcpl/Development/git/MyTestedApplication/app/build/test-report/debug/index.html
:app:test
BUILD SUCCESSFUL
Troubleshooting
alternative source directories
Just like you can have your java source files somewhere else you can move your test source files. Just update gradle sourceSets
config.
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
androidTest {
setRoot('tests')
}
}
package org.junit does not exist
You forgot to add the junit test dependency in the app build script
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile 'org.robolectric:robolectric:2.3'
androidTestCompile 'junit:junit:4.10'
}
java.lang.RuntimeException: Stub!
You're running this test with the run configurations from Android Studio instead of command line (Terminal tab in Android Studio). To run it from Android Studio you'll have to update the app.iml
file to have the jdk entry listed at the bottom. See deckard-gradle example for details.
Complete error example:
!!! JUnit version 3.8 or later expected:
java.lang.RuntimeException: Stub!
at junit.runner.BaseTestRunner.<init>(BaseTestRunner.java:5)
at junit.textui.TestRunner.<init>(TestRunner.java:54)
at junit.textui.TestRunner.<init>(TestRunner.java:48)
at junit.textui.TestRunner.<init>(TestRunner.java:41)
at com.intellij.rt.execution.junit.JUnitStarter.junitVersionChecks(JUnitStarter.java:190)
at com.intellij.rt.execution.junit.JUnitStarter.canWorkWithJUnitVersion(JUnitStarter.java:173)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:56)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
ERROR: JAVA_HOME is set to an invalid directory
See this SO question for a solution. Add the below export to you bash profile:
export JAVA_HOME=`/usr/libexec/java_home -v 1.7`
The complete error log:
ERROR: JAVA_HOME is set to an invalid directory: export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation.
Test Class not found
If you want to run your tests from Android Studio Junit Test runner instead you'll have to expand the build.gradle file a little more so that android studio can find your compiled test classes:
sourceSets {
testLocal {
java.srcDir file('src/test/java')
resources.srcDir file('src/test/resources')
}
}
android {
// tell Android studio that the instrumentTest source set is located in the unit test source set
sourceSets {
instrumentTest.setRoot('src/test')
}
}
dependencies {
// Dependencies for the `testLocal` task, make sure to list all your global dependencies here as well
testLocalCompile 'junit:junit:4.11'
testLocalCompile 'com.google.android:android:4.1.1.4'
testLocalCompile 'org.robolectric:robolectric:2.3'
// Android Studio doesn't recognize the `testLocal` task, so we define the same dependencies as above for instrumentTest
// which is Android Studio's test task
androidTestCompile 'junit:junit:4.11'
androidTestCompile 'com.google.android:android:4.1.1.4'
androidTestCompile 'org.robolectric:robolectric:2.3'
}
task localTest(type: Test, dependsOn: assemble) {
testClassesDir = sourceSets.testLocal.output.classesDir
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split('/')
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
sourceSets.testLocal.compileClasspath += files(buildDir)
sourceSets.testLocal.runtimeClasspath += files(buildDir)
}
classpath = sourceSets.testLocal.runtimeClasspath
}
check.dependsOn localTest
from: http://kostyay.name/android-studio-robolectric-gradle-getting-work/
Some more resources
The best articles I found around this are:
java.srcDirs = ['src/main/java','src/instrumentTest/java']
in thesourceSets
section of thebuild.gradle
– Rob Holmes/src/androidTest/java
folder with robolectric JUnit tests, see: stackoverflow.com/a/25473702/1406325 – Flo