android - 单元测试室 android - 这项工作尚未完成
问题描述
我目前正在对使用 Room 的本地数据源进行单元测试。我创建了一个测试类:
/**
* Integration test for the [WatchListLocalDataSource].
*/
@RunWith(AndroidJUnit4::class)
@MediumTest
class WatchListLocalDataSourceTest {
private lateinit var sut: WatchListLocalDataSourceImpl
private lateinit var database: ShowsDatabase
private lateinit var entityMapper: ShowEntityMapper
private lateinit var testDispatcher: TestCoroutineDispatcher
private lateinit var testScope: TestCoroutineScope
@Before
fun setup() {
entityMapper = ShowEntityMapper()
testDispatcher = TestCoroutineDispatcher()
testScope = TestCoroutineScope(testDispatcher)
val context = InstrumentationRegistry.getInstrumentation().context
// using an in-memory database for testing, since it doesn't survive killing the process
database = Room.inMemoryDatabaseBuilder(
context,
ShowsDatabase::class.java
)
.setTransactionExecutor(testDispatcher.asExecutor())
.setQueryExecutor(testDispatcher.asExecutor())
.build()
sut = WatchListLocalDataSourceImpl(database.watchListDao(), entityMapper)
}
@After
@Throws(IOException::class)
fun cleanUp() {
database.close()
}
@Test
@Throws(Exception::class)
fun observeWatchedShows_returnFlowOfDomainModel() = testScope.runBlockingTest {
val showId = 1
sut.addToWatchList(mockShow(showId))
val watchedShows: List<Show> = sut.observeWatchedShows().first()
assertThat("Watched shows should contain one element", watchedShows.size == 1)
assertThat("Watched shows element should be ${mockShow(showId).name}", watchedShows.first() == mockShow(showId))
}
}
但是,测试没有完成,请注意:
java.lang.IllegalStateException: This job has not completed yet
中的实际方法sut
是:
override suspend fun addToWatchList(show: Show) = withContext(Dispachers.IO) {
watchListDao.insertShow(WatchedShow(entityMapper.mapFromDomainModel(show)))
}
解决方案
所以问题从addToWatchList
数据源中的方法开始,我明确地将它与 Dipachers.IO 协程范围不同,这是不必要的,因为如果您suspend
为函数使用关键字,Room 会在内部处理线程。
这产生了一个问题,在测试协程范围上开始的工作正在生成一个新范围,并且由于房间需要在启动它的同一线程上完成,因此出现了死锁,从而产生了java.lang.IllegalStateException: This job has not completed yet
错误。
解决方案是:
withContext
在 DAO 插入方法中删除,让 Room 自己处理范围。- 在测试类的 @Before 方法中添加
.allowMainThreadQueries()
到数据库构建器,这允许空间使用提供的测试范围并确保所有工作都在该定义的范围内进行。
正确的代码是:
@Before
fun setup() {
entityMapper = ShowEntityMapper()
testDispatcher = TestCoroutineDispatcher()
testScope = TestCoroutineScope(testDispatcher)
val context = InstrumentationRegistry.getInstrumentation().context
// using an in-memory database for testing, since it doesn't survive killing the process
database = Room.inMemoryDatabaseBuilder(
context,
ShowsDatabase::class.java
)
.setTransactionExecutor(testDispatcher.asExecutor())
.setQueryExecutor(testDispatcher.asExecutor())
// Added this to the builder
|
v
.allowMainThreadQueries()
.build()
sut = WatchListLocalDataSourceImpl(database.watchListDao(), entityMapper)
}
在 dataSource 类中:
override suspend fun addToWatchList(show: Show) {
watchListDao.insertShow(WatchedShow(entityMapper.mapFromDomainModel(show)))
}
推荐阅读
- angular - Angular 没有重载匹配这个调用
- javascript - 如何使用 am4chart 更改图表颜色
- r - facet_wrap 中的不同色点取决于标准
- python - 我们可以使用网站管理员提取其他网站分析数据吗?
- c# - 如何配置 .net 核心以将所有公共方法视为 noneAction 方法,除了那些用 HttpVerb 过滤器装饰的方法
- java - listFiles() 在 /storage/emulated 上返回 null,而 /storage/emulated/0 存在
- javascript - 使用时刻检查字符串是否为有效日期
- android - Android:为什么即使对象为空,数据绑定也不强制 NullPointerException
- sqlite - 如何阅读 Chrome Cookie 文件?
- julia - Julia:如何获取 NamedTuple 的名称?