单元测试室 android - 此作业尚未完成
Unit testing Room android - This job has not completed yet
我目前正在对使用 Room 的本地数据源进行单元测试。
我创建了一个测试 class:
/**
* 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
关键字为您的功能。
这产生了一个问题,即在测试协程作用域上开始的工作正在生成一个新的作用域,并且由于空间需要在它开始的同一个线程上完成,因此出现了一个死锁,导致 java.lang.IllegalStateException: This job has not completed yet
错误。
解决方案是:
- 删除 DAO 插入方法中的
withContext
,让 Room 自己处理范围。
- 将
.allowMainThreadQueries()
添加到测试的 @Before 方法中的数据库构建器 class,这允许在提供的测试范围内工作并确保所有工作都在该定义的范围内进行.
正确的代码是:
@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)
}
并且在数据源中 class:
override suspend fun addToWatchList(show: Show) {
watchListDao.insertShow(WatchedShow(entityMapper.mapFromDomainModel(show)))
}
我目前正在对使用 Room 的本地数据源进行单元测试。 我创建了一个测试 class:
/**
* 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
关键字为您的功能。
这产生了一个问题,即在测试协程作用域上开始的工作正在生成一个新的作用域,并且由于空间需要在它开始的同一个线程上完成,因此出现了一个死锁,导致 java.lang.IllegalStateException: This job has not completed yet
错误。
解决方案是:
- 删除 DAO 插入方法中的
withContext
,让 Room 自己处理范围。 - 将
.allowMainThreadQueries()
添加到测试的 @Before 方法中的数据库构建器 class,这允许在提供的测试范围内工作并确保所有工作都在该定义的范围内进行.
正确的代码是:
@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)
}
并且在数据源中 class:
override suspend fun addToWatchList(show: Show) {
watchListDao.insertShow(WatchedShow(entityMapper.mapFromDomainModel(show)))
}