Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test assets not available #2065

Open
jonneymendoza opened this issue Sep 29, 2015 · 19 comments
Open

Test assets not available #2065

jonneymendoza opened this issue Sep 29, 2015 · 19 comments

Comments

@jonneymendoza
Copy link

Hi i am using roboletreci version 3.0 for unit testing my app and have the asssets folder with some files inside under src/test/assets but keep getting fileNotFound.

Here is my test code:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, manifest = Config.NONE)
public class ShowsDatesTest {

@Test
public void testResponse(){
BufferedReader = getBufferedResponseFromFile("response.json");
}

private BufferedReader getBufferedResponseFromFile(String json) throws IOException {

         mContext = RuntimeEnvironment.application;
        InputStream jsonResponse = mContext.getAssets().open(json);

        BufferedReader br = new BufferedReader(new InputStreamReader(jsonResponse, "UTF-8"));

        return br;

    }

}

Here is my sourceset and dependencies from my gradle file:

 production {
            res.srcDir 'build-config/production/res'
            test.java.srcDirs += 'src/main/java'
            test.java.srcDirs += "build/generated/source/r/production"
            test.java.srcDirs += "build/generated/source/buildConfig/production"
            test.assets.srcDir file('src/test/assets')
        }

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:19.1.+'
    compile 'com.google.code.gson:gson:2.3'
    testCompile('org.robolectric:robolectric:3.0') {
        exclude group: 'commons-logging', module: 'commons-logging'
        exclude group: 'org.apache.httpcomponents', module: 'httpclient'
    }
    compile 'com.fasterxml.jackson:jackson-parent:2.5'
    compile 'com.squareup:otto:1.3.6'
    compile 'com.jakewharton:butterknife:6.1.0'
    compile 'com.sothree.slidinguppanel:library:3.0.0'
    compile 'com.crashlytics.android:crashlytics:1.+'
    compile 'com.mcxiaoke.volley:library-aar:1.0.0'
    compile 'joda-time:joda-time:2.8.2'
    testCompile('junit:junit:4.12') {
        exclude module: 'hamcrest'
        exclude module: 'hamcrest-core'
    }
    testCompile 'org.hamcrest:hamcrest-all:1.3'
    compile 'com.sothree.slidinguppanel:library:3.0.0'
    compile 'com.squareup:otto:1.3.6'
    compile 'com.squareup.okhttp:okhttp:2.3.0'
    testCompile 'org.apache.maven:maven-ant-tasks:2.1.3'

    compile 'com.google.android.gms:play-services:7.0.0'

    compile 'com.android.support:multidex:1.0.0'

    compile 'com.android.support:recyclerview-v7:21.0.+'
    compile 'com.squareup.picasso:picasso:2.5.2'

production is a build varient

@erd
Copy link
Member

erd commented Oct 10, 2015

Is src/test/assets supported by the Android Gradle plugin? I don't see any mention of it here: http://tools.android.com/tech-docs/unit-testing-support

@jaredsburrows
Copy link
Contributor

I have seen this before. Using getAssets().open works but getAssets().openFd does not.

This works:

    @Test
    public void test_play_once() throws Exception {
        assertThat(application.getAssets().open("21.wav"), not(nullValue()));
    }

@erd
Copy link
Member

erd commented Oct 14, 2015

For what it's worth, you can also put test fixtures in src/test/resources and load them with ClassLoader.getResourceAsStream.

@nebiros
Copy link

nebiros commented Dec 11, 2015

@erd how can you share a fixture file, ex: a json file from an api response to mock, between test and androidTest?, I try doing this: https://gist.github.com/nebiros/4d6370c3ba87f3dd6f6a but doesn't work, robolectric try to look at build/intermidiates/… folder. Any clue?.

@jffiorillo
Copy link

I have same behaviour as @nebiros !! Any clue ? Thanks

@nebiros
Copy link

nebiros commented Feb 29, 2016

@jffiorillo I found this way, to share fixture files from test to androidTest:

  • Add your fixture files to the resources folder of your test one, here: src/test/resources.
  • Add test resources folder to androidTest, resources.srcDirs += ['src/test/resources'], here's an example:
android {
    sourceSets {
        String sharedTestJavaDir = 'src/sharedTest/java'

        test {
            java.srcDirs += [sharedTestJavaDir]
        }
        androidTest {
            java.srcDirs += [sharedTestJavaDir]
            resources.srcDirs += ['src/test/resources']
        }
    }
}
  • Access fixture files from your androidTest env this way: this.getClass().getClassLoader().getResourceAsStream(filename);

@peetron
Copy link

peetron commented Jun 10, 2016

Has anyone had any progress with this?

@jffiorillo
Copy link

Thanks @nebiros !

@markproxy
Copy link

markproxy commented Apr 17, 2018

EmojiCompat, when used with BundledEmojiCompatConfig, looks for NotoColorEmojiCompat.ttf in the assets directory. If you use gradle implementation "com.android.support:support-emoji-bundled:$supportVersion", this font file is automatically unpacked into the build/intermediates/assets folder for your particular build variant, and initializing EmojiCompat works as expected. But I'm trying to use the BundledEmojiCompatConfig only with my unit tests, and I've discovered that testImplementation "com.android.support:support-emoji-bundled:$supportVersion", does not unpack the font file into the build/intermediates directory tree. As a result, the font asset cannot be loaded at runtime, and EmojiCompat cannot be initialized.

As a work-around, I'm trying to explicitly place a copy of NotoColorEmojiCompat.ttf into the assets directory as part of my test build. However, as others have found, files in the src/test/assets directory are not included in the build output, and therefore cannot be found at runtime. I briefly experimented with custom sourceSets in my build.gradle file, but I couldn't get it to work as desired, and it feels like there must be a better way. Has anyone come up with a solution for getting test-only assets into the test build? (Putting my font in the resources folder isn't going to work; BundledEmojiCompatConfig looks for the font asset in the root of the assets folder.)

UPDATE: When I run ./gradlew sourceSets, I see that there are no assets directories for test, where there are for androidTest. I suspect it's just not possible to populate the assets directory for unit tests, which means BundledEmojiCompatConfig can't be used unless you include it in your main (non-test) build.

The same test, as an androidTest, just works. For now, it looks like EmojiCompat and Robolectric are not easily made to cooperate. (I can't figure out why my normal FontRequestEmojiCompatConfig doesn't work with Robolectric. For some reason, it just never gets out of EmojiCompat.LOAD_STATE_LOADING.)

@flocsy
Copy link

flocsy commented May 1, 2018

@nebiros I am trying to use your solution. When I run the test in src/androidTest I can see that the resources I'd like to reach were copied to build/intermediates/sourceFolderJavaResources/androidTest/developMock/debug/foo and I'm trying to "find it" using: MyClass.class.getClassLoader().getResource("foo") or getClass().getClassLoader().getResource("foo") [these work from tests under src/test] but the classLoader doesn't find "foo"

@jongerrish
Copy link
Contributor

We've revamped resources in Robolectric 4.0 to process the binary resource files + arsc table produced by aapt and the resource handling code is now much closer to that of the framework. This includes changes to parse the merged manifest using the Android platform code.

Mind giving this a try with 4.0 + binary resources by configuring your gradle.build as follows:-

testImplementation "org.robolectric:robolectric:4.0"

android {
  enableUnitTestBinaryResources=true
  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

We're doing a big bug scrub and trying to be aggressive about it so apologies if this was closed in error, in which case feel free to reopen.

If this is still an issue an example project with failing test would be most helpful.

@calvarez-ov
Copy link

calvarez-ov commented Jan 8, 2019

Ran into this when wanting to test Room db migration from test instead of androidTest. The following files need to be present to run the migration test:
app/src/main/assets/com.example.app.MyDatabase/*.json

Found this workaround which will copy assets from app/src/test/assets to the generated assets folder of the app. Then they'll be picked up for tests. Could be useful when running only tests, but ⚠️ could pose a problem if you run tests, and then build the apk without a clean first (the assets would be included the apk).

In the app gradle file:

afterEvaluate {
    tasks.findByName("packageDebugUnitTestForUnitTest").doFirst {
        File testAssetDir = new File("${project.projectDir}/src/test/assets")
        File appAssetDir = new File("${project.buildDir}/intermediates/merged_assets/debug/mergeDebugAssets/out")
        GFileUtils.copyDirectory(testAssetDir, appAssetDir)
    }
}

To prevent the assets from going into the apk, they can be excluded by removing the files before packageDebug:

    tasks.findByName("packageDebug").doFirst {
        File dir = new File("${project.buildDir}/intermediates/merged_assets/debug/mergeDebugAssets/out/com.example.app.MyDatabase")
        GFileUtils.deleteDirectory(dir)
    }

This config may be more complex if you run tests with different flavors. Here I just assume running tests with testDebugUnitTest.

Note, when using the gradle task to copy the assets, I didn't succeed at excluding them from the apk with packagingOptions or aaptOptions.

Note, this is using RobolectricTestRunner. I didn't experiment yet with the new AndroidJUnit4.


A less convoluted approach (no gradle task customization) could be:

  • Place the needed assets directly in app/src/main/assets
  • Exclude them from the apk with aaptOptions

But then you end up with some files in your main sourceset which are only needed for tests. 🤷‍♀️

@xian
Copy link
Member

xian commented Jan 8, 2019

Reopening, test assets ought to be merged for aapt.

@xian xian reopened this Jan 8, 2019
@xian xian changed the title File not found getting a file from Assets folder Test assets not available Jan 8, 2019
@WildOrangutan
Copy link

I've encountered similar issue, when trying to use assets from main source set in tests, while using a flavor that also has it's own assets. This occurs only when target sdk is set below 28.
Here's a project I've used to bruteforce my solution out of it, if anyone cares:
https://github.com/WildOrangutan/RobolectricMissingAssets

@zawadz88
Copy link

@calvarez-ov FYI we are testing Room migrations with Robolectric 4.3 and it seems to be getting the assets as expected. We just had to add:

sourceSets {
    test.assets.srcDirs += files("$projectDir/schemas".toString())
}

in android {} in build.gradle

@bopbi
Copy link

bopbi commented May 15, 2020

@calvarez-ov FYI we are testing Room migrations with Robolectric 4.3 and it seems to be getting the assets as expected. We just had to add:

sourceSets {
    test.assets.srcDirs += files("$projectDir/schemas".toString())
}

in android {} in build.gradle

@zawadz88 could you share your build.gradle and the migration test class configuration, since i cannot make it run although i have follow your way

@zawadz88
Copy link

Hi @bopbi,
I cannot share the entire files but here are the relevant parts I think:

android {

    testOptions {
        
        unitTests {
            //fixes https://github.com/robolectric/robolectric/issues/3169#issuecomment-312046322
            includeAndroidResources = true
        }
    }

    sourceSets {
        test.assets.srcDirs += files("$projectDir/schemas".toString())
    }
}

kapt {
    arguments {
        arg("room.schemaLocation", "$projectDir/schemas".toString())
    }
}

Migration class fragment:

@RunWith(MyDbRobolectricRunner::class)
class RoomDatabaseMigrationsTest {

    @Rule
    @JvmField
    val helper: MigrationTestHelper = MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(),
        MyRoomDatabase::class.java.canonicalName,
        FrameworkSQLiteOpenHelperFactory()
    )

    @Test
    @Throws(IOException::class)
    fun `Migrate from 1 to 2 persisting correct data`() {
        // given
        helper.createDatabase(TEST_DB, 1).apply {
            insertData(createData(), this)
            close()
        }

        // when
        helper.runMigrationsAndValidate(TEST_DB, 2, true, *MIGRATIONS)

        // then
        // verify DB content after migration
    }

    private fun insertData(dbModel: MyDbModel, db: SupportSQLiteDatabase) {
        val values = ContentValues()
        values.put(ID, dbModel.localId)
        // put other values

        db.insert(MY_TABLE_NAME, SQLiteDatabase.CONFLICT_REPLACE, values)
    }

    private fun getMigratedRoomDatabase(): MyRoomDatabase {
        val database = Room.databaseBuilder(
            ApplicationProvider.getApplicationContext(),
            MyRoomDatabase::class.java,
            TEST_DB
        )
            .addMigrations(*MIGRATIONS)
            .allowMainThreadQueries()
            .build()
        helper.closeWhenFinished(database)
        return database
    }
}

@sdelaysam
Copy link

sdelaysam commented Jun 9, 2021

@zawadz88 doesn't seem to work with the latest AGP - test.assets.srcDirs has no effect.
Still an option to use debug.assets.srcDirs for testing debug build.
However can't find proper way for release build testing.
Setting up release.assets.srcDirs makes database schemas leak into release APK.

@haydenkaizeta
Copy link

Any updates here? As the above comment says I see no effect with test.assets.srcDirs. My current solution is debug.assets.srcDirs but this is suboptimal because there are assets I need for test that I don't want in the debug build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests