Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用LiveData、协同程序和事务测试Android Room_Android_Testing_Kotlin_Android Room_Kotlin Coroutines - Fatal编程技术网

使用LiveData、协同程序和事务测试Android Room

使用LiveData、协同程序和事务测试Android Room,android,testing,kotlin,android-room,kotlin-coroutines,Android,Testing,Kotlin,Android Room,Kotlin Coroutines,我想测试我的数据库层,我发现自己陷入了一种第22条军规的境地 测试用例由两部分组成: 保存一些实体 加载实体并断言数据库映射按预期工作 简而言之,问题在于: Insert是一个suspend方法,这意味着它需要在runBlocking{} Query返回结果的LiveData,该结果也是异步的。因此,需要加以观察。有一个问题可以解释如何做到这一点 但是,为了根据上面的链接观察LiveData,我必须使用InstantTaskExecutorRule。(否则我会得到java.lang.Ill

我想测试我的数据库层,我发现自己陷入了一种第22条军规的境地

测试用例由两部分组成:

  • 保存一些实体
  • 加载实体并断言数据库映射按预期工作
简而言之,问题在于:

  • Insert
    是一个
    suspend
    方法,这意味着它需要在
    runBlocking{}
  • Query
    返回结果的
    LiveData
    ,该结果也是异步的。因此,需要加以观察。有一个问题可以解释如何做到这一点
  • 但是,为了根据上面的链接观察LiveData,我必须使用
    InstantTaskExecutorRule
    。(否则我会得到
    java.lang.IllegalStateException:无法在后台线程上调用ObserveForver。
  • 这适用于大多数情况,但不适用于带注释的DAO方法。测试永远不会结束。我认为它在等待某个事务线程时陷入僵局
  • 删除
    InstantTaskExecutorRule
    可以让事务插入方法完成,但是我无法断言其结果,因为我需要规则来观察数据
详细说明 我的
Dao
类如下所示:

@Dao
interface GameDao {
    @Query("SELECT * FROM game")
    fun getAll(): LiveData<List<Game>>

    @Insert
    suspend fun insert(game: Game): Long

    @Insert
    suspend fun insertRound(round: RoundRoom)

    @Transaction
    suspend fun insertGameAndRounds(game: Game, rounds: List<RoundRoom>) {
        val gameId = insert(game)
        rounds.onEach {
            it.gameId = gameId
        }

        rounds.forEach {
            insertRound(it)
        }
    }

测试此场景的正确方法是什么?在开发数据库映射层时,我需要这些类型的测试,以确保一切都如我所期望的那样工作

问题在于事务本身在内部某处使用runBlocking,这会导致死锁。 我已将InstantTaskExecutorRule更改为此类:

class IsMainExecutorRule : TestWatcher() {

    val defaultExecutor = DefaultTaskExecutor()

    override fun starting(description: Description?) {
        super.starting(description)
        ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
            override fun executeOnDiskIO(runnable: Runnable) {
                defaultExecutor.executeOnDiskIO(runnable)
            }

            override fun postToMainThread(runnable: Runnable) {
                defaultExecutor.executeOnDiskIO(runnable)
            }

            override fun isMainThread(): Boolean {
                return true
            }
        })
    }

    override fun finished(description: Description?) {
        super.finished(description)
        ArchTaskExecutor.getInstance().setDelegate(null)
    }
}
然后在代码中,它将是:

@get:Rule
val liveDataRule = IsMainExecutorRule()

它不会导致死锁,但仍然允许观察LiveData。

现在有了解决此问题的方法,如中所述

修复程序正在向内存库数据库生成器中的文件室添加一行:

db = Room
    .inMemoryDatabaseBuilder(context, AppDatabase::class.java)
    .setTransactionExecutor(Executors.newSingleThreadExecutor()) // <-- this makes all the difference
    .build()
db=房间
.inMemoryDatabaseBuilder(上下文,AppDatabase::class.java)

.setTransactionExecutor(Executors.newSingleThreadExecutor())//这是使用实时数据和协同程序访问测试室的唯一方法。很快,谷歌将发布新的测试库来解决这些问题。听到这个消息很难过。您是否碰巧与他们表示将在何处解决此问题有关?这是用于测试实时数据的google示例代码。另一个此解决方案似乎解决了此问题,但没有解释其工作的确切原因,也没有说明它是否会影响其他与生命周期相关的测试(如LiveData)。如果没有缺点和副作用,那么为什么不是默认的测试方法呢?你能详细说明一下@vbevans94吗?
@get:Rule
val liveDataRule = IsMainExecutorRule()
db = Room
    .inMemoryDatabaseBuilder(context, AppDatabase::class.java)
    .setTransactionExecutor(Executors.newSingleThreadExecutor()) // <-- this makes all the difference
    .build()