如何对Realm迁移进行单元测试?

时间:2016-07-13 13:48:13

标签: android realm

我试图在Realm上对迁移进行单元测试。我的主要问题是:如何维护RealmObject的不同模式版本,以便能够创建旧对象的实例,进行迁移,然后根据新的模式版本检查它是否正确?

我开始尝试保留不同的模式版本,但它不会编译,因为对象具有相同的名称,尽管它们位于不同的包中。

2 个答案:

答案 0 :(得分:8)

在Realm中,我们通过将旧的Realm文件存储为资产(请参阅https://github.com/realm/realm-java/tree/master/realm/realm-library/src/androidTest/assets)来测试迁移机制,然后编写测试以在迁移测试后检查结果(请参阅https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RealmMigrationTests.java)。

答案 1 :(得分:2)

Realm source code以及this project的帮助下,我找到了如何执行Realm迁移单元测试的方法。

一步一步,这是你构建测试的方式:

  • 从特定状态创建Realm实例。过去的某个架构版本(您要迁移的旧架构)
  • 按如下方式创建RealmConfiguration :( 1)将模式版本升级到项目的最新版本(2)保持与上面的Realm实例相同的名称(3)添加将运行迁移的迁移代码。
  • 从上面创建的RealmConfiguration创建一个Realm实例。如果迁移成功运行,则不会抛出任何异常。您将成功将代码从该状态迁移到当前应用程序的Realm架构版本。

当您第一次开始构建应用时,假设您的应用以前在架构版本0上运行。现在,您在架构版本1上进行了一些更改。您正在编写测试以查看是否可以从版本0成功迁移到版本1.

在版本0创建Realm数据库的Realm文件。我通常做的是使用我的应用程序使用版本0时创建的git提交来检查我的代码,运行我的应用程序来创建Realm数据库,然后using the bash script I mention in this post我将我的Realm文件复制到我的计算机文件系统。将此域文件存储在应用项目的androidTest/assets assets目录中。

TestRealmConfigurationFactory文件复制到您的项目中。这是您将用于从androidTest/assets Realm文件创建Realm实例的内容。

创建测试。以下是我运行的迁移测试中的一些示例代码:

@RunWith(AndroidJUnit4::class)
open class MigrationTest {

    @get:Rule open val configFactory = TestRealmConfigurationFactory()
    @get:Rule open val thrown = ExpectedException.none()

    private lateinit var context: Context

    @Before
    fun setup() {
        context = InstrumentationRegistry.getInstrumentation().context
    }

    @Test(expected = RealmMigrationNeededException::class)
    @Throws(Exception::class)
    fun migrate_migrationNeededIsThrown() {
        val REALM_NAME = "0.realm"
        val realmConfig = RealmConfiguration.Builder()
                .name(REALM_NAME)
                .schemaVersion(0)
                .build()
        configFactory.copyRealmFromAssets(context, REALM_NAME, realmConfig)

        // should fail because my code base realm models have changed *since* this 0.realm file.
        // When you want to get a realm instance, it will take what realm objects are already in memory (mapped by the "name" property of the RealmConfiguration) and it will compare it to the models in the application. If they are different, a realm migration exception will be thrown. So, you need to make sure to add Realm migrations to your code at all times.
        val realm = Realm.getInstance(realmConfig)
        realm.close()
    }

    @Test fun migrate_migrateFrom0toLatest() {
        val REALM_NAME = "0.realm"
        val realmConfig = RealmConfiguration.Builder()
                .name(REALM_NAME)
                .schemaVersion(RealmInstanceManager.schemaVersion)
                .migration { dynamicRealm, oldVersion, newVersion ->
                    val schema = dynamicRealm.schema

                    for (i in oldVersion until newVersion) {
                        RealmInstanceManager.migrations[i.toInt()].runMigration(schema)
                    }
                }
                .build()
        configFactory.copyRealmFromAssets(context, REALM_NAME, realmConfig)

        val realm = Realm.getInstance(realmConfig)
        realm.close()
    }

    // convenient method to generate 1 realm file in app directory to be able to copy to assets directory for the next migration test when schema version changes.
    @Test fun createFileForCurrentVersionToCopyToAssetsFile() {
        val REALM_NAME = "${RealmInstanceManager.schemaVersion}.realm"
        val realmConfig = RealmConfiguration.Builder()
                .name(REALM_NAME)
                .schemaVersion(RealmInstanceManager.schemaVersion)
                .build()

        Realm.deleteRealm(realmConfig)
        val realm = Realm.getInstance(realmConfig)
        realm.close()
    }

}

作为参考,这是我的RealmInstanceManager和我创建的其他配套文件,用于隔离我的迁移。

open class RealmInstanceManager(private val userManager: UserManager) {

    companion object {
        val migrations: List<RealmSchemaMigration> = listOf(
                Migration1()
        )

        var schemaVersion: Long = 0L
            get() = migrations.size.toLong()
    }

}

我的Migration1课程:

class Migration1: RealmSchemaMigration {

    override fun runMigration(schema: RealmSchema) {
        schema.get(OwnerModel::class.java.simpleName)!!
                .addField("avatar_url", String::class.java)
                .setRequired("avatar_url", true)
                .transform { it.set("avatar_url", "") }
    }

}

最后,我的RealmSchemaMigration界面:

interface RealmSchemaMigration {

    fun runMigration(schema: RealmSchema)

}

我发现上面的文件和接口配置是管理我的项目中的迁移文件以运行测试以及应用程序本身的好方法。

真的,你已经完成了。迁移测试位于函数migrate_migrateFrom0toLatest()中,其中我从架构版本0获取我的Realm文件资产(名为&#34; 0.realm&#34;)并将其迁移到我的代码库中的最新版本。函数createFileForCurrentVersionToCopyToAssetsFile()不是必需的,但我喜欢它,因为在我运行测试之后,我可以将我新设备的Realm文件复制到我的资产目录中,用于我运行的下一个迁移测试。更方便,然后我必须做我上面解释的git checkout方法。