0

There are a ton questions regarding getResource or getResourceAsStream returning null and so far I understand the issue but I currently cannot properly solve it.

I have a resource file which is used by some class in production. The file is located in

app\src\main\res\raw\some.def

The class SomeManager uses this to access this file:

InputStream stream = SomeClass.class.getResourceAsStream("/res/raw/some.def");

This succeeds when running the debug variant of the application on the emulator and it also succeeds when running the debug variant of the instrumented tests. I assume because the resource is properly packaged into the jar?

However when I run some local jUnit tests in android studio this resource is not found. I did not fully understand what is exactly executed when running a local test and I am not sure how to provide the resource file in a way that can be loaded in a test.

I would like to avoid doubling this resource file because it is actually something I want to test, I also would like to not change the getResourceAsStream path because this is the production file I want to test.

I am using gradle and android studio if that matters.

Samuel
  • 6,126
  • 35
  • 70
  • I don't understand what you want to test here? Or do you just want to understand, what's the mechanism behind all this and why it doesn't work from a technical point of view? So what is the purpose of your test? – Bevor Feb 16 '18 at 18:22
  • @Bevor Sorry for being confusing. I cannot execute my unit test because it uses `SomeManager` which tries to load a resource located at `/res/raw` and I would like to provide the resources for my local tests as clean as possible – Samuel Feb 16 '18 at 18:30
  • Does you debug configuration changes the classpath ?...getResourceAsStreams completely works on the classpath...there is a mismatch of classpath between your debug and run variants.. – saurav Feb 17 '18 at 02:11

1 Answers1

0

I debugged this issue with Sysinternal's Process Monitor and realized that when I run code locally on my machine the resources are as streams are looked up from various different locations on disk. One of those locations is <build_directory>/intermediate/classes/<build_type> where it is obviously missing.

The solution to this was to create a copy task that performs the copying and make it robust enough to work for all build types.

So I modified my app's gradle file and added those dynamic tasks:

android.buildTypes.all{ theBuildType ->
    task "copyDebugAssets_${theBuildType.name}"(type: Copy){

        from "${projectDir}/src/main/res"
        into "${buildDir}/intermediates/classes/${theBuildType.name}/res"
        eachFile { println it.name }
    }
}

tasks.whenTaskAdded { task ->

    // println "A message which is logged at QUIET level ${task.name}"
    if (task.name.startsWith("process") && task.name.endsWith("Resources")) {

        def partInBetween = task.name.substring("process".length(), task.name.indexOf("Resources")).toLowerCase()
        if (partInBetween == "debugandroidtest") {
            return
        }
        def dependentTask = "copyDebugAssets_${partInBetween}"
        println "Registering ${dependentTask} to ${task.name} for config '${partInBetween}'"
        task.dependsOn dependentTask
    }

}

I have really no idea on how to properly use gradle but the first statement generates as many copyDebugAssets_xxx tasks as there are build types. After syncing you can see and execute the in the gradle projects.

To avoid calling them whenever a clean or rebuild is done manually, the second part registers the copyDebugAssets_xxx copy tasks to the various process<Configuration>Resources tasks, which are then called automatically. So far I can run local unit tests in multiple build type successfully.

Samuel
  • 6,126
  • 35
  • 70