使用Room @Transaction函数时Android Instrumentation测试无法执行完成
看起来你碰到了Room旧版本结合协程测试时的典型问题,结合你用的AS 3.4.1和Android 9模拟器,我给你几个方向来排查和解决:
1. 优先检查Room版本兼容性
AS 3.4.1默认搭配的Room版本大概率是2.1.x系列,这个版本对suspend函数和@Transaction的组合支持还不够完善,容易出现线程阻塞或者死锁的情况,导致测试无预警终止。
建议你把Room相关依赖升级到2.2.x及以上的稳定版本(要保证room-runtime、room-compiler、room-testing等所有组件版本一致),比如在你的build.gradle(Module级别)里修改:
def room_version = "2.2.6" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" testImplementation "androidx.room:room-testing:$room_version" implementation "androidx.room:room-ktx:$room_version" // 别忘了这个协程支持库
2. 避免Dao中的函数名称冲突
看你的代码,带@Transaction的insert函数内部还调用了另一个同名的insert(插入bookEntry.entity的那个),这种同名函数在Room生成代理实现时可能会出现逻辑冲突,尤其是加上@Transaction注解后。
建议给这两个函数重命名,明确区分功能:
@Transaction suspend fun insertBookEntry(bookEntry: BookEntry): Long { val id = insertBookEntryEntity(bookEntry.entity) bookEntry.embeddedContacts?.apply { forEach { it.id = 0; it.bookEntryId = id } }?.let { insertContacts(it) } return id } // 重命名插入entity的函数 @Insert suspend fun insertBookEntryEntity(entity: BookEntryEntity): Long // 重命名插入联系人的函数 @Insert suspend fun insertContacts(contacts: List<Contact>)
3. 检查测试线程配置
虽然你用了InstantTaskExecutorRule,但Room的事务在测试环境下可能需要更明确的线程配置。可以尝试在初始化测试数据库时临时允许主线程查询(仅用于排查,正式代码不推荐),看看是否能解决问题:
// 在DatabaseTest的setup里修改数据库初始化代码 db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) .allowMainThreadQueries() // 临时添加这个配置 .build()
如果这样测试能正常运行,说明是线程调度的问题,你可以改用TestCoroutineDispatcher来统一测试中的线程调度,或者升级Room版本来解决。
4. 更换测试环境
Android 9的模拟器ART虚拟机可能存在一些旧版本的bug,尝试更换到Android 10及以上的模拟器,或者直接用真机测试,看问题是否消失。另外,AS 3.4.1也是比较旧的版本,升级到AS 4.0+也能修复不少环境相关的问题。
按照这个顺序排查,应该能很快定位到问题所在。
内容的提问来源于stack exchange,提问作者the_dani

