Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test resources folder with Robolectric and Gradle

I want to add some test files to my test folder because I want to simulate some JSON file loading. The problem is that last Robolectric version (2.4) looks like it doesn't find the test-resources path (I get a NPE), but with Robolectric 2.3 it does.

This is how my project looks like:

enter image description here

In the Res class (used by the tests), I load the JSON that way:

public static String str(String path) throws IOException {
    InputStream is = null;
    try {
        is = Res.class.getResourceAsStream(path); // "is" IS NULL!
        return new String(ByteStreams.toByteArray(is)); // ERROR HERE
    } finally {
        if (is != null) {
            is.close();
        }
    }
}

public static JsonReader json(String path) throws IOException {
    return new JsonReader(new StringReader(str("/contact.json")));
}

And that is my build.gradle file:

buildscript {
    // ...
    dependencies {
        classpath 'org.robolectric:robolectric-gradle-plugin:1.0.1'
    }
}
apply plugin: 'com.android.application'
apply plugin: 'org.robolectric'

robolectric {
    // ...
    include '**/*Test.class'
    ignoreFailures true
    // ...
}

android {
    compileSdkVersion 21
    buildToolsVersion "22"

    defaultConfig {
        applicationId "com.ph"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 9
        versionName '4.1'
    }

    sourceSets {
        androidTest {
            setRoot('src/test')
            res.srcDirs = [ 'src/test/resources' ]
        }

        test {
            setRoot('src/test')
            res.srcDirs = [ 'src/test/resources' ]
        }
    }
}

dependencies {
    compile 'com.android.support:support-v4:22.0.0'
    compile 'joda-time:joda-time:2.7'
    compile 'com.google.guava:guava:18.0'
    compile files('libs/gcm.jar')
    compile 'org.codehaus.jackson:jackson-core-asl:1.9.13'
    compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
    compile 'org.slf4j:slf4j-api:1.7.10'
    compile 'com.jakewharton:butterknife:6.1.0'

    provided 'com.squareup.dagger:dagger-compiler:1.2.2'
    compile 'com.squareup.dagger:dagger:1.2.2'

    // ================== TESTING LIBRARIES ======================
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:1.10.19'
    testCompile ('org.robolectric:robolectric:2.4') {
        exclude module: 'classworlds'
        exclude module: 'commons-logging'
        exclude module: 'httpclient'
        exclude module: 'maven-artifact'
        exclude module: 'maven-artifact-manager'
        exclude module: 'maven-error-diagnostics'
        exclude module: 'maven-model'
        exclude module: 'maven-project'
        exclude module: 'maven-settings'
        exclude module: 'plexus-container-default'
        exclude module: 'plexus-interpolation'
        exclude module: 'plexus-utils'
        exclude module: 'wagon-file'
        exclude module: 'wagon-http-lightweight'
        exclude module: 'wagon-provider-api'
    }
    testCompile 'com.squareup:fest-android:1.0.+'
    testCompile 'org.bouncycastle:bcprov-jdk15on:1.50'
    testCompile 'com.jakewharton:butterknife:6.1.0'
    testCompile 'com.google.android:android:4.1.1.4'
}

And that is the error message:

java.lang.NullPointerException
at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:210)
at com.google.common.io.ByteStreams.copy(ByteStreams.java:65)
at com.google.common.io.ByteStreams.toByteArray(ByteStreams.java:115)
at com.ph.Res.str(Res.java:17)
at com.ph.Res.json(Res.java:26)
at com.ph.SyncTest.testContact(SyncTest.java:182)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

Any idea what is wrong with Robolectric 2.4? Or is it better to rollback to 2.3?

like image 212
David Avatar asked Oct 31 '22 08:10

David


1 Answers

I guess your issue is related to an open issue at google. https://code.google.com/p/android/issues/detail?id=136013

There exist also a workaround here an unmodified copy from post #10

import groovy.json.StringEscapeUtils
gradle.projectsEvaluated {
def variants = android.applicationVariants.collect()

tasks.withType(Test) { task ->
    try {
        variants.each { variant ->
            def buildTypeName = variant.buildType.name.capitalize()

            def productFlavorNames = variant.productFlavors.collect { it.name.capitalize() }
            if (productFlavorNames.isEmpty()) {
                productFlavorNames = [""]
            }
            def productFlavorName = productFlavorNames.join('')
            def flavor = StringUtils.uncapitalize(productFlavorName)

            def variationName = "${productFlavorName}${buildTypeName}"

            if (task.name.contains(variationName)) {
                def variationPath = variant.buildType.name;

                if (StringUtils.isNotEmpty(productFlavorName)) {
                    variationPath = StringUtils.uncapitalize(productFlavorName) + "/" + variationPath
                }

                def copyTestResourcesTask = project.tasks.create("copyTest${variationName}Resources", Copy)
                copyTestResourcesTask.from("${projectDir}/src/test/resources")
                copyTestResourcesTask.into("${buildDir}/intermediates/classes/test/${variationPath}")

                // Makes the test task depend on the copy test resource variation task
                task.dependsOn(copyTestResourcesTask)

                variants.remove(variant)

                throw new Exception("Break") // Breaking the loop
            }
        }
    } catch (Exception e) {} // Just drop the exception
}
}

and here you can find a working example https://github.com/nenick/android-gradle-template/blob/master/App/build.gradle

like image 198
nenick Avatar answered Nov 12 '22 17:11

nenick