Edit Module build.gradle
.
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
}
dependencies {
// existing
implementation "android.arch.persistence.room:runtime:$android_room_version"
kapt "android.arch.persistence.room:compiler:$android_room_version"
// new
androidTestImplementation "android.arch.persistence.room:testing:$android_room_version"
}
NOTE: Make sure exportSchema = true
for @Database, else schema json won't be generated and later you might bump into FileNotFoundException: Cannot find the schema file in the assets folder
.
@Database(entities = [(Pin::class)], version = 1, exportSchema = true)@TypeConverters(RoomConverters::class)abstract class AppDatabase : RoomDatabase() { abstract fun pinDao(): PinDao}
When you compile the app, assets/[DatabaseClassPath]
(Android view) or /app/schemas/[DatabaseClassPath]
(Project view) is generated with 1.json
, 2.json
, etc. (the number is the database version).
Perform Android Room database migration.
NOTE: You might want to generate 1.json
by performing a compilation first, then only perform the necessary database migration (add new column/table to @Entity
and change database version) to generate 2.json
.
Create MigrationTest.kt
in androidTest
.
@RunWith(AndroidJUnit4::class)class MigrationTest { companion object { private const val TAG = "MigrationTest" private const val TEST_DB = "test-db" } lateinit var db: SupportSQLiteDatabase @Rule @JvmField val helper: MigrationTestHelper = MigrationTestHelper(InstrumentationRegistry.getInstrumentation(), AppDatabase::class.java!!.canonicalName, FrameworkSQLiteOpenHelperFactory()) @Before fun setUp() { db = helper.createDatabase(TEST_DB, 1) } @Test fun migrate1To2() { // db has schema version 1. insert some data using SQL queries. // You cannot use DAO classes because they expect the latest schema. // db.execSQL(...); val values = ContentValues() values.put("id", "test01") values.put("name", "Test 01") val id = db.insert("pin", SQLiteDatabase.CONFLICT_REPLACE, values) // Prepare for the next version. db.close() // Re-open the database with version 2 and provide // MIGRATION_1_2 as the migration process. db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2) // MigrationTestHelper automatically verifies the schema changes, // but you need to validate that the data was migrated properly. val cursor = db.query("SELECT * FROM pin WHERE rowid = ?", arrayOf(id)) MatcherAssert.assertThat(cursor, Is(notNullValue())) MatcherAssert.assertThat(cursor.moveToFirst(), Is(true)) val name = cursor.getString("name") MatcherAssert.assertThat(name, Is("Test 01")) val isLocationAccurate = cursor.getIntOrNull("is_location_accurate") // MatcherAssert.assertThat(isLocationAccurate, Is(nullValue())) MatcherAssert.assertThat(isLocationAccurate, Is(0)) }}
NOTE: Refer to Android Instrumented Unit Test if you are not familiar with Instrumented Unit Test.
NOTE: If you bump into duplicate column name
exception, check schema/1.json
and schema/2.json
to make sure the new column doesn't exist in schema/1.json
.
References: