Android Room 2.3.0迁移测试触发onPreMigrate AbstractMethodError问题
问题根因
你遇到的AbstractMethodError是Room 2.3.0版本的已知问题,该版本的room-testing组件内的MigrationTestHelper.MigratingDelegate没有实现同一版本Room核心库新增的onPreMigrate抽象方法,该bug在2.4.0及以上版本已官方修复。
依赖配置问题解答
官方给出的依赖配置没有错误,但是对应不同测试场景:
testImplementation引入的依赖仅用于纯JVM环境的本地单元测试- Room迁移测试属于必须运行在Android设备/模拟器上的instrumented测试,因此必须将
room-testing的依赖声明改为androidTestImplementation "androidx.room:room-testing:$room_version",你的判断是正确的。
完整可运行的Room迁移测试示例
第一步:依赖配置
建议优先将Room版本升级到2.4.0及以上,避免上述bug,build.gradle配置如下:
def room_version = "2.4.3" // 生产环境依赖 implementation "androidx.room:room-runtime:$room_version" // Java环境用annotationProcessor,Kotlin环境用kapt kapt "androidx.room:room-compiler:$room_version" // 迁移测试依赖,必须用androidTestImplementation androidTestImplementation "androidx.room:room-testing:$room_version" // 设备端测试基础依赖 androidTestImplementation "androidx.test.ext:junit:1.1.5" androidTestImplementation "androidx.test:runner:1.5.2"
如果必须使用2.3.0版本,可参考文末的临时兼容方案。
第二步:编写迁移逻辑
// 1升2的迁移逻辑,根据你的业务修改SQL语句 val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { // 示例:给user表新增age、address两列 database.execSQL("ALTER TABLE user ADD COLUMN age INTEGER NOT NULL DEFAULT 0") database.execSQL("ALTER TABLE user ADD COLUMN address TEXT") } } // 将迁移逻辑添加到数据库构建配置中 Room.databaseBuilder(context, AppDatabase::class.java, "app_db") .addMigrations(MIGRATION_1_2) .build()
第三步:编写迁移测试类
测试类必须放在src/androidTest/java/你的应用包名目录下:
@RunWith(AndroidJUnit4::class) class RoomMigrationTest { private val TEST_DB = "migration_test_db" // 注册MigrationTestHelper规则 @get:Rule val helper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory() ) @Test fun testMigration1To2() { // 1、创建版本1的数据库,插入测试数据 helper.createDatabase(TEST_DB, 1).apply { execSQL("INSERT INTO user (id, name) VALUES (1, '测试用户')") close() } // 2、执行迁移并自动验证表结构是否符合版本2的Entity定义 val db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2) // 3、可选:验证迁移后数据完整性 val cursor = db.query("SELECT id, name, age, address FROM user WHERE id = 1") assert(cursor.moveToFirst()) assert(cursor.getInt(cursor.getColumnIndexOrThrow("age")) == 0) assert(cursor.getString(cursor.getColumnIndexOrThrow("address")) == null) cursor.close() db.close() } }
2.3.0版本临时兼容方案
如果无法升级Room版本,可在androidTest目录下添加如下补丁类,替换测试中使用的MigrationTestHelper即可:
class FixedMigrationTestHelper( instrumentation: Instrumentation, dbClassName: String?, openFactory: SupportSQLiteOpenHelper.Factory ) : MigrationTestHelper(instrumentation, dbClassName, openFactory) { override fun createDatabase(name: String, version: Int): SupportSQLiteDatabase { val db = super.createDatabase(name, version) val delegateField = RoomOpenHelper::class.java.getDeclaredField("delegate") delegateField.isAccessible = true val originDelegate = delegateField.get(helper) as RoomOpenHelper.Delegate val proxyDelegate = Proxy.newProxyInstance( originDelegate.javaClass.classLoader, arrayOf(RoomOpenHelper.Delegate::class.java) ) { _, method, args -> if (method.name == "onPreMigrate") return@newProxyInstance Unit method.invoke(originDelegate, *args.orEmpty()) } delegateField.set(helper, proxyDelegate) return db } }
内容的提问来源于stack exchange,提问作者Suau
相关产品推荐
相关产品推荐

