Robolectric with Gradle: Resources not found

20,514

Solution 1

I was running across this same issue and this is what I came up with. Instead of creating a separate project for the tests, I created a source set for the Robolectric tests and added a new task that "check" would depend on. Using some of the code from your question, here are the relevant bits of the (working) build file:

apply plugin: 'android'

sourceSets {
    testLocal {
        java.srcDir file('src/test/java')
        resources.srcDir file('src/test/resources')
    }
}

dependencies {
    compile 'org.roboguice:roboguice:2.0'
    compile 'com.google.android:support-v4:r6'

    testLocalCompile 'junit:junit:4.8.2'
    testLocalCompile 'org.robolectric:robolectric:2.1'
    testLocalCompile 'com.google.android:android:4.0.1.2'
    testLocalCompile 'com.google.android:support-v4:r6'
    testLocalCompile 'org.roboguice:roboguice:2.0'
}

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

I've included my dependencies block to point out that in order for me to get this up and going, I had to repeat all of my compile dependencies in my custom testLocal source set.

Running gradle testLocal builds and runs just the tests inside of src/test/java, while running gradle check runs these tests in addition to those in the default android instrumentTest source set.

Hope this helps!

Solution 2

Update: Jake Wharton just announced the gradle-android-test-plugin. You can find it at https://github.com/square/gradle-android-test-plugin

It seems to be pretty streamlined, especially if you plan to use robolectric.


Old Answer Below

The robolectric-plugin looks promising.

The sample build.gradle file they provide is :

buildscript {
    repositories {
        mavenCentral()
        maven {
            url "https://oss.sonatype.org/content/repositories/snapshots"
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.4.2'
        classpath 'com.novoda.gradle:robolectric-plugin:0.0.1-SNAPSHOT'
    }
}

apply plugin: 'android'
apply plugin: 'robolectric'

repositories {
    mavenCentral()
    mavenLocal()
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    //compile files('libs/android-support-v4.jar')

    // had to deploy to sonatype to get AAR to work
    compile 'com.novoda:actionbarsherlock:4.3.2-SNAPSHOT'

    robolectricCompile 'org.robolectric:robolectric:2.0'
    robolectricCompile group: 'junit', name: 'junit', version: '4.+'
}

android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"

    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 17
    }
}

It doesn't seem to work with the Android Gradle plugin version 0.5 but maybe it will soon.

Share:
20,514
passy
Author by

passy

Software Engineer at Facebook, previously Twitter.

Updated on August 18, 2020

Comments

  • passy
    passy almost 4 years

    I'm trying to run my Robolectric tests together with the new Gradle Android build system, but I'm stuck at accessing the resources of my main project.

    I split the build into two separate projects to avoid conflicts between the java and the android gradle plugins, so the directory structure looks roughly like this:

    .
    ├── build.gradle
    ├── settings.gradle
    ├── mainproject
    │   ├── build
    │   │   ├── classes
    │   │   │   └── debug
    │   ├── build.gradle
    │   └── src
    │       └── main
    │           ├── AndroidManifest.xml
    │           └── ...
    └── test
        ├── build.gradle
        └── src
            └── test
                └── java
                    └── ...
                        └── test
                            ├── MainActivityTest.java
                            ├── Runner.java
                            ├── ServerTestCase.java
                            └── StatusFetcherTest.java
    

    My build.gradle in test/ currently looks like this:

    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath 'com.stanfy.android:gradle-plugin-java-robolectric:2.0'
        }
    }
    
    apply plugin: 'java-robolectric'
    
    repositories {...}
    
    javarob {
        packageName = 'com.example.mainproject'
    }
    
    test {
        dependsOn ':mainproject:build'
        scanForTestClasses = false
        include "**/*Test.class"
        // Oh, the humanity!
        def srcDir = project(':mainproject').android.sourceSets.main.java.srcDirs.toArray()[0].getAbsolutePath()
        workingDir srcDir.substring(0, srcDir.lastIndexOf('/'))
    }
    
    project(':mainproject').android.sourceSets.main.java.srcDirs.each {dir ->
        def buildDir = dir.getAbsolutePath().split('/')
        buildDir =  (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
    
        sourceSets.test.compileClasspath += files(buildDir)
        sourceSets.test.runtimeClasspath += files(buildDir)
    }
    
    dependencies {    
        testCompile group: 'com.google.android', name: 'android', version: '4.1.1.4'
        testCompile group: 'org.robolectric', name: 'robolectric', version: '2.0-alpha-3'
        ...
    }
    

    The evil classpath hackery allows me to access all classes of my main project, except for R, which exists as .class file in the build directory, but raises this error during the compileTestJava task:

    /.../MainActivityTest.java:16: error: cannot find symbol
                    final String appName = activity.getResources().getString(R.string.app_name);
                                                                              ^
      symbol:   variable string
      location: class R
    1 error
    :test:compileTestJava FAILED
    

    There must be a better way to execute Robolectric tests with the new build system, right?

    (Full source of the app)

  • passy
    passy almost 11 years
    This looks like a huge step into the right direction, thanks! The Robolectric testrunner is executing, however, I'm now getting a "android.content.res.Resources$NotFoundException: unknown resource xxx" when anything tries to access a resource. Full stacktrace here: gist.github.com/passy/255bbd42ada11ad5fba7
  • passy
    passy almost 11 years
    This seems to come from the testrunner being unable to find the AndroidManifest.xml, which lies in src/main. I guess I could set those paths in the a RobolectricTestRunner subclass, but with Eclipse I would just set the working directory to the main project. Can I do that in the gradle file?
  • user2457888
    user2457888 almost 11 years
    @passy Unfortunately I haven't seen that issue come up. However, you should be able to put your AndroidManifest.xml in the root of the project directory and override the manifest source set by doing something similar to this in build.gradle: android { sourceSets { main { manifest.srcFile 'AndroidManifest.xml' } } } Source: tools.android.com/tech-docs/new-build-system/user-guide
  • passy
    passy almost 11 years
    Thanks again, @user2457888. :) I set the workingDir inside the localTest task to src/main and now it finds the AndroidManifest.xml and all resources.
  • alex
    alex almost 11 years
    I have everything working from gradle, but the IDE complains because it can't find the imports. How did you add the dependencies to the project in the ide? Did you create a module? How did you configure it?
  • ligi
    ligi almost 11 years
    thanks - that looks good - unfortunately I get "Could not find method testLocalCompile() for arguments [junit:junit:4.8.2] on project" - any Idea on how to solve this?
  • Mark
    Mark almost 11 years
    I got this working, and merged with the "old" example from github.com/novoda/robolectric-plugin , and so was able to remove the duplicate dependency calls. I'm able to run it from the command line just fine. Getting Android Studio to see it, as alex mentioned, is a problem. I was able to set src/test/main as a root test folder - but the Studio can't find junit, robolectric, etc. test dependencies.
  • Mark
    Mark almost 11 years
    @alex - I figured it out! Go into the module settings, in dependencies, add a new library, choose from maven, and add robolectric. Change the scope on it to compile. Works for now, although it might interfere if you're using instrumentationTest as well.
  • ebernie
    ebernie almost 11 years
    @Mark do you mind sharing how you were able to set src/test/main as a root test folder? If I configure this via the IDE, it keeps resetting itself to a non-src dir, presumably because it's not set in my build.gradle.
  • Mark
    Mark almost 11 years
    You right click on it, and mark it as test root - I forget the exact menu sequence. But you're right - it does keep resetting itself. It looks like there's no way to have it stick.
  • Thomas Kaliakos
    Thomas Kaliakos almost 11 years
    This is really cool...but I have a project that I ported from Eclipse into Android Studio and therefore has the old folder structure: (Project folder/ src, res, libs, gen ...) . How would that affect the localTest and specifically the buildDir variable?
  • Maragues
    Maragues almost 11 years
    Hi user2457888, could you upload a sample project to a public repo? I'm unable to compile the test project, I could really use some real code. Also, have you tested it for Gradle 0.5.+? Thanks!
  • Colin M.
    Colin M. over 10 years
    I'm anxiously awaiting Jake's plugin to support resources split by buildType. Currently it can't seem to deal with that. Someone wrote a patch to fix this (a few months ago) but there's been no comments on it and has not been accepted. Until then, the method in the accepted answer is working.
  • Colin M.
    Colin M. over 10 years
    Any idea how this could be updated to include the yet-to-be-standard aar format as a dependency? See my related issue here: stackoverflow.com/questions/21099754/…
  • Flame
    Flame about 10 years
    Seems like Jake Wharton's plugin is now deprecated?
  • Saad Farooq
    Saad Farooq about 10 years
    Yes it is. There seems to be a flurry to move to the standard android test infrastructure. Robolectric is still helpful for some cases but I found it easier to manage the application so that most of my pure java classes are java library modules that are unit tested with junit and Android specific ones with the android test framework.
  • Colin M.
    Colin M. over 9 years
    Anyone have luck modifying this method to work with flavors? When I add flavors, the testLocal fails not being able to find any of my classes, even though there's no classes needed from the flavors for the tests.
  • Cris
    Cris over 5 years
    Both old and new answer reference deprecated plugins