Testing Room as JUnit Test Not AndroidTest

Viewed 1043

Trying to test room migration using the MigrationTestHelper class and Robolectric. We want it as a JUnit test because our CI environment cannot fire up an emulator. (Please no answers with CI fixes for emulators, CI is not in my control) Only issue I have is that the test fails because it can't find the schemas. My build.gradle has this in it already

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

    test {
        java.srcDirs += "$projectDir/src/sharedTest/java"
    }

    testOptions {
        unitTests {
            includeAndroidResources = true
        }
        unitTests.all {
            systemProperty 'robolectric.enabledSdks', '21'
        }
    }
}
dependencies {
   // has all the proper dependencies from mockito adn robolectric to kotlin and junit.
}

Here is the test code but again its mostly just the schema can't be found when the database creation is called. Also the json files are there in the schema directory

@RunWith(RobolectricTestRunner::class)
class Migration19To20Test {
    private val migration = MyDatabase.MIGRATION_19_20
    private val fromVersion = 19
    private val toVersion = 20

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

    private val testDatabaseName = "migration-test"

    @Test
    fun insertsFirmwareVersionFullColumn() {
        givenADatabase()

        val validateDroppedTables = true
        val db = helper.runMigrationsAndValidate(
                testDatabaseName,
                toVersion,
                validateDroppedTables,
                migration)

        db.query("select * from ${DatabaseConstants.Table.People}").use { cursor ->
            cursor.moveToFirst()
            assertTrue("table should contain the ${DatabaseConstants.Column.People.NAME_FULL} column as it should have been added",
                    cursor.columnNames.contains(DatabaseConstants.Column.People.NAME_FULL))
        }
    }

    private fun givenADatabase() {
        // Test fails here
        helper.createDatabase(testDatabaseName, fromVersion)
    }
}
2 Answers

I solved it by copying MigrationTestHelper to my test source code and modified loadSchema method to look like this

    private SchemaBundle loadSchema(Context context, int version) throws IOException {
//        InputStream input = context.getAssets().open(mAssetsFolder + "/" + version + ".json");
        InputStream input = new FileInputStream("./schemas/" + mAssetsFolder + "/" + version + ".json");
        return SchemaBundle.deserialize(input);
    }

The schemas directory is configured in build.gradle

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [
                        "room.schemaLocation"  : "$projectDir/schemas".toString(),
                        "room.incremental"     : "true",
                        "room.expandProjection": "true"]
            }
        }
    }
}

As you can see the cause is that this class looks for migration in assets directory because Google assumes that you will run those migration on device. In such case you must include them as part of android assets.

With Robolectric it's just reading from a directory and putting that into a stream.

My scenario was not exactly the same - I had an instrumented migration test case - but perhaps the cause is the same. If you have something like the rule below, aapt will strip the schema files and it will do so in debug builds as well as release ones.

buildTypes {

  debug {...}

  release {
    aaptOptions {
      ignoreAssetsPattern '!*.json'
    }
  }
}
Related