简体   繁体   中英

How to mock android room withTransaction method with Mockk

I'm trying to make some unit tests for my business logic. I have repository in which I save to room database (2.1.0-rc01) some data from response. Data saving into different tables with different dao in single transaction. Code is simplified:

ItemRepository

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)
    }
}

For unit test I'm using Mockk library. How can I mock room withTransaction method?. withTransaction is declared as

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

I'm trying to writing test

@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)
    }
}

You first have to enable static mocks for the Android Room KTX method withTransaction {} . You also need to capture the suspend lambda function passed to it. This captured function can just be invoked so the code inside it runs. Since you're mocking all the database calls, you don't need a real transaction here.

@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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM