How to access files from assets folder during tests execution?
Solution 1
Basically you have to use Context
to read assets. You can not load assets with ClassLoader
since it is not in a classpath. I am not sure how you run Robolectric test cases. Here are how I can achieve in both Android studio and gralde command.
I added separate app-unit-test module to run Robolectric test cases in app project.
With proper build configuration and custom RobolectricTestRunner
, following test case will pass.
@Config
@RunWith(MyRobolectricTestRunner.class)
public class ReadAssetsTest {
@Test
public void test_ToReadAssetsFileInAndroidTestContext() throws IOException {
ShadowApplication application = Robolectric.getShadowApplication();
Assert.assertNotNull(application);
InputStream input = application.getAssets().open("b.xml");
Assert.assertNotNull(input);
}
}
app-unit-test/build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.14.1'
}
}
apply plugin: 'java'
evaluationDependsOn(':app')
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
repositories {
maven { url "$System.env.ANDROID_HOME/extras/android/m2repository" } // Fix 'com.android.support:*' package not found issue
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
testCompile 'junit:junit:4.8.2'
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'
exclude group: 'com.android.support', module: 'support-v4'
}
testCompile('com.squareup:fest-android:1.0.+') {
exclude group: 'com.android.support', module: 'support-v4'
}
testCompile 'org.mockito:mockito-core:1.10.10'
def appModule = project(':app')
testCompile(appModule) {
exclude group: 'com.google.android'
exclude module: 'dexmaker-mockito'
}
testCompile appModule.android.applicationVariants.toList().first().javaCompile.classpath
testCompile appModule.android.applicationVariants.toList().first().javaCompile.outputs.files
testCompile 'com.google.android:android:4.1.1.4'
/* FIXME : prevent Stub! error
testCompile files(appModule.plugins.findPlugin("com.android.application").getBootClasspath())
*/
compile project(':app')
}
Add custom RobolectricTestRunner to adjust file paths. Look at the assets path.
public class MyRobolectricTestRunner extends RobolectricTestRunner {
private static final String APP_MODULE_NAME = "app";
/**
* Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file
* and res directory by default. Use the {@link org.robolectric.annotation.Config} annotation to configure.
*
* @param testClass the test class to be run
* @throws org.junit.runners.model.InitializationError if junit says so
*/
public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
System.out.println("testclass="+testClass);
}
@Override
protected AndroidManifest getAppManifest(Config config) {
String userDir = System.getProperty("user.dir", "./");
File current = new File(userDir);
String prefix;
if (new File(current, APP_MODULE_NAME).exists()) {
System.out.println("Probably running on AndroidStudio");
prefix = "./" + APP_MODULE_NAME;
}
else if (new File(current.getParentFile(), APP_MODULE_NAME).exists()) {
System.out.println("Probably running on Console");
prefix = "../" + APP_MODULE_NAME;
}
else {
throw new IllegalStateException("Could not find app module, app module should be \"app\" directory in the project.");
}
System.setProperty("android.manifest", prefix + "/src/main/AndroidManifest.xml");
System.setProperty("android.resources", prefix + "/src/main/res");
System.setProperty("android.assets", prefix + "/src/androidTest/assets");
return super.getAppManifest(config);
}
}
I followed this blog to do it.
- http://blog.blundellapps.com/android-gradle-app-with-robolectric-junit-tests/
- http://blog.blundellapps.com/how-to-run-robolectric-junit-tests-in-android-studio/
Full example codes are here.
Solution 2
I've just got stuck with the same issue and here is how it works for me.
I put test files to src/test/resources
folder instead of assets folder.
Then I get these files as stream in following way
private InputStream openFile(String filename) throws IOException {
return getClass().getClassLoader().getResourceAsStream(filename);
}
filename
is the relative path to the file inside resources
folder.
That's it. I've found solution at Robolectric github
Solution 3
With the latest Android Instrumentation Test, you can just use this:
InstrumentationRegistry.getContext().getAssets().open(filePath);
Solution 4
Updating as for Roboelectric 3.1
@Test
public void shouldGetJSONFromAsset() throws Exception{
Assert.assertNotNull(RuntimeEnvironment.application); //Getting the application context
InputStream input = RuntimeEnvironment.application.getAssets().open("fileName.xml");// the file name in asset folder
Assert.assertNotNull(input);
}
See also
de Guide
Solution 5
When Robolectric versions previous to 4.0 we need to take in count that:
By default, Robolectric will assume that your resources and assets are located in directories named res and assets, respectively. These paths are assumed to be relative to the directory where the manifest is located. You can change these values by adding the resourceDir and assetDir options to the @Config annotation.
See the official docs
Related videos on Youtube
Eugene
Updated on April 06, 2022Comments
-
Eugene about 2 years
How to access files from assets folder during unit tests execution? My project is build using Gradle, I use Robolectric to run tests. It seems like gradle is being recognizing the
assets
:This is how I'm struggling to read the file:
public String readFileFromAssets(String fileName) throws IOException { InputStream stream = getClass().getClassLoader().getResourceAsStream("assets/" + fileName); Preconditions.checkNotNull(stream, "Stream is null"); BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); return IOUtils.toString(reader); }
But
stream
is alwaysnull
. I tried it many different ways, i.e. defined path to a file using different approaches.Thank you very much in advance.
-
Peter Niederwieser over 9 yearsDoes this apply? stackoverflow.com/questions/20184480/…
-
Eugene over 9 yearsNo, it isn't InstrumentationTestCase
-
Peter Niederwieser over 9 yearsWell, perhaps it needs to be.
-
HpTerm over 9 yearsI never used Unit Test but probably the problem is the same as my answer here : stackoverflow.com/questions/9544737/read-file-from-assets/… accessing the asset folder is not possible directly, you have to use getAssets() to have it working. However as I never used Unit Test, my comment is perhaps irrelevant. In that case tell me and I will remove it.
-
Qw4z1 over 9 yearsCheck this question stackoverflow.com/questions/24631428/…
-
-
Eugene over 9 yearsthis way they would compile in application which I need to avoid.
-
nebiros over 8 yearsDoesn't work for me,
RuntimeEnvironment.application.getAssets().open
returns anIOException
, and I add assets folder as: gist.github.com/nebiros/4d6370c3ba87f3dd6f6a. -
Bao-Long Nguyen-Trong about 8 yearsWorks for me. However, this opens files in the assets folder of the main sourceset and not the test one (which is what I am looking for).
-
Joakim Engstrom almost 5 yearsThis
InstrumentationRegistry
is deprecated so this is not the correct syntax anymore. -
WindRider over 4 yearsThis is no longer actual in the current version (4.0).
-
JuanMoreno over 4 yearsThe new replacement for
androidx.test.InstrumentationRegistry
isandroidx.test.platform.app.InstrumentationRegistry
see here developer.android.com/reference/androidx/test/platform/app/…