2

我正在尝试为我的业务逻辑进行一些单元测试。我有存储库,我将响应中的一些数据保存到房间数据库(2.1.0-rc01)。在单个事务中将数据保存到具有不同 dao 的不同表中。代码简化:

项目存储库

suspend fun saveItems(response: Response) {
    val items = response.items.map { it.toLocalItem() }
    val subItems = response.items.flatMap { item ->
            item.subItems.map { it.toLocal(item.id) }
        }

    db.withTransaction {
        db.itemDao().deleteAll()
        db.itemDao().insertAll(items)
        db.subItemDao().insertAll(subItems)
    }
}

对于单元测试,我使用的是 Mockk 库。如何使用Transaction 方法模拟房间?withTransaction 被声明为

suspend fun <R> RoomDatabase.withTransaction(block: suspend () -> R): R

我正在尝试编写测试

@MockK
private lateinit var database: AppDatabase
@MockK
private lateinit var itemDao: ItemDao
@MockK
private lateinit var subItemDao: SubItemDao


@Test
fun checkSaveItems() = runBlocking {
    repository = ItemRepository(database)
    coEvery { database.itemDao() } returns itemDao
    coEvery { database.subItemDao() } returns subItemDao

    //TODO: execute database.withTransaction(block: suspend () -> R)

    coEvery { itemDao.deleteAll() } just Runs
    coEvery { itemDao.insertAll(any()) } just Runs
    coEvery { subItemDao.insertAll(any()) } just Runs

    repository.saveItems(testResponse)

    coVerifySequence {
        itemDao.deleteAll()
        itemDao.insertAll(testItems)
        subItemDao.insertAll(testSubItems)
    }
}
4

2 回答 2

9

您首先必须为 Android Room KTX 方法withTransaction {}启用静态模拟。您还需要捕获传递给它的挂起 lambda 函数。可以调用这个捕获的函数,以便它内部的代码运行。由于您正在模拟所有数据库调用,因此您不需要在这里进行真正的事务。

@Before
fun initMocks() {
  MockKAnnotations.init(this)

  mockkStatic(
            "androidx.room.RoomDatabaseKt"
  )

  val transactionLambda = slot<suspend () -> R>()
    coEvery { db.withTransaction(capture(transactionLambda)) } coAnswers {
      transactionLambda.captured.invoke()
    }
}

You should then be able to run your code as written.

于 2019-06-18T15:30:01.953 回答
0

To expand on Andrew's answer, the mockk documentation for extension functions shows that if you are mocking an object wide or class wide extension function, you can just use regular mockk to achieve that. However, if you are using a module wide extension function, like withTransaction, you also need to perform mockkStatic on the module's class name.

于 2021-05-17T13:16:54.563 回答