我想测试我的数据库层,但陷入了22类情况。
测试用例由两部分组成:
总而言之,问题在于:
Insert
是一种suspend
方法,这意味着它需要在runBlocking{}
中运行Query
返回结果的LiveData
,这也是异步的。因此,需要对其进行观察。有一个this这样的问题可以解释如何做到这一点。InstantTaskExecutorRule
。 (否则我得到java.lang.IllegalStateException: Cannot invoke observeForever on a background thread.
)@Transaction
注释的DAO方法。测试永远不会结束。我认为它在等待某些事务线程时陷入僵局。InstantTaskExecutorRule
可以完成Transaction-Insert方法,但是我不能断言其结果,因为我需要规则才能观察数据。我的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)
}
}
测试用例是:
@RunWith(AndroidJUnit4::class)
class RoomTest {
private lateinit var gameDao: GameDao
private lateinit var db: AppDatabase
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
@Before
fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room.inMemoryDatabaseBuilder(
context, AppDatabase::class.java
).build()
gameDao = db.gameDao()
}
@Test
@Throws(Exception::class)
fun storeAndReadGame() {
val game = Game(...)
runBlocking {
gameDao.insert(game)
}
val allGames = gameDao.getAll()
// the .getValueBlocking cannot be run on the background thread - needs the InstantTaskExecutorRule
val result = allGames.getValueBlocking() ?: throw InvalidObjectException("null returned as games")
// some assertions about the result here
}
@Test
fun storeAndReadGameLinkedWithRound() {
val game = Game(...)
val rounds = listOf(
Round(...),
Round(...),
Round(...)
)
runBlocking {
// This is where the execution freezes when InstantTaskExecutorRule is used
gameDao.insertGameAndRounds(game, rounds)
}
// retrieve the data, assert on it, etc
}
}
getValueBlocking
是LiveData
的扩展功能,几乎是从上面的链接复制粘贴的
fun <T> LiveData<T>.getValueBlocking(): T? {
var value: T? = null
val latch = CountDownLatch(1)
val observer = Observer<T> { t ->
value = t
latch.countDown()
}
observeForever(observer)
latch.await(2, TimeUnit.SECONDS)
return value
}
测试这种情况的正确方法是什么?在开发数据库映射层时,我需要这些类型的测试,以确保一切正常。
答案 0 :(得分:3)
this answer中介绍了解决此问题的方法。
此修复程序是向Room内存数据库构建器添加了一行:
db = Room
.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.setTransactionExecutor(Executors.newSingleThreadExecutor()) // <-- this makes all the difference
.build()
使用单线程执行器,测试将按预期工作。
答案 1 :(得分:2)
问题在于事务本身在内部某处使用 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()
它不会导致死锁,但仍然可以观察实时数据。