使用 Room @Transaction 函数时,Android 仪器测试不会运行到结束

the*_*ani 7 android kotlin android-room android-architecture-components kotlin-coroutines

我正在使用 AS 3.4.1 和运行 Android 9 的模拟器进行测试。

当我Room Dao Function annotated with @Transaction在其中使用 a 时,以下测试将无法运行。

class RecurrenceManagerTest : DatabaseTest() {

    @Rule
    @JvmField
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    var recurringEntryId: Long = -1

    @Before
    override fun setup() {
        super.setup() // only initialized the db

        val recurringEntry = RecurringEntry(
            recurrence = Recurrence(DATE.toEpochMilli(), Recurrence.DAILY)
        )

        recurringEntryId = runBlocking { db.recurringEntryDao().insert(recurringEntry) }

        val recurringBookEntry = BookEntry.create(
            title = TITLE,
            date = DATE,
            value = VALUE,
            isPaid = IS_PAID,
            notes = NOTES,
            entryType = ENTRY_TYPE,
            categoryId = CATEGORY_ID,
            contacts = CONTACTS,
            recurringEntryId = recurringEntryId
        )

        runBlocking {
            db.bookEntryDao().insert(recurringBookEntry) // BreakPoint #1
        }
    }

    @Test
    fun testInsertRecurrencesAndSchedule() { 
        var recurringEntry = runBlocking { db.recurringEntryDao().get(recurringEntryId) } // BreakPoint #2

        assertThat(recurringEntry, notNullValue())

        runBlocking { RecurrenceManager.insertRecurrencesAndSchedule(ApplicationProvider.getApplicationContext(), db, recurringEntry!!) }

        val bookEntries = db.bookEntryDao().getBookEntries().liveDataValue()
    }
}
Run Code Online (Sandbox Code Playgroud)

这是插入的函数:

@Transaction
suspend fun insert(bookEntry: BookEntry): Long {
    val id = insert(bookEntry.entity)
    bookEntry.embeddedContacts?.apply {
        forEach {
            it.id = 0
            it.bookEntryId = id
        }
    }?.let {
        insert(it)
    }

    return id
}
Run Code Online (Sandbox Code Playgroud)

因此,如果我像这样运行测试(参见 BreakPoint #1),BreakPoint #2 甚至不会被调用,所以测试在某个地方之前结束,没有结果。

如果我用完全相同的代码替换 BreakPoint #1 处的代码,则该insert函数具有,测试运行正确。

有谁知道这里有什么问题?

sum*_*tta 19

您可以使用setTransactionExecutor在另一个线程中运行事务

return Room
       .inMemoryDatabaseBuilder(context, MyRoomDatabase::class.java)
       .setTransactionExecutor(Executors.newSingleThreadExecutor())
       .build()
Run Code Online (Sandbox Code Playgroud)

然后在测试时使用runBlocking而不是 runBlockingTest

@Test
fun moveItem() = runBlocking {
    transactionFunction()
}
Run Code Online (Sandbox Code Playgroud)

  • 伟大的!对于 kotlinx.coroutines 1.6.0 的用户:也适用于 **runTest** (3认同)

Hak*_*ied 6

我遇到了同样的问题,问题是因为InstantTaskExecutorRule,如果删除下面的代码块,@Transaction应该可以很好地使用suspend关键字

@Rule
@JvmField
val instantTaskExecutorRule = InstantTaskExecutorRule()
Run Code Online (Sandbox Code Playgroud)

看来这个规则阻止了RoomDatabase获取事务线程。在RoomDatabase.kt执行过程中,以下函数被阻止:

private suspend fun Executor.acquireTransactionThread(controlJob: Job): ContinuationInterceptor
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!