我正试图编写每隔一段时间就会得到一些当前数据的类,这样它就会将一个流转换为另一个流。我在为它写测试时卡住了。
当我运行一个测试时,它通过了,但当我运行所有测试时:testA总是通过,但testB或testC失败,出现以下错误:
kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 60000 ms, the test coroutine is not completing, there were active child jobs
类:
interface Time {
val currentTime: Flow<Int>
}
data class Data(val value: Int)
interface DataRepository {
fun getCurrentData(): Data
}
我的主类:
@Singleton
class CurrentDataProvider(time: Time, repository: DataRepository) {
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private var lastUpdate = Int.MIN_VALUE
val currentData: SharedFlow<Data> =
time.currentTime
.transform {
if (lastUpdate + 10 <= it) {
lastUpdate = it
emit(repository.getCurrentData())
}
}
.onCompletion {
coroutineScope.cancel() // Is it necessary?
}
.shareIn(
scope = coroutineScope,
started = SharingStarted.Eagerly,
)
}
和测试:
class Test {
private val time = mock<Time>()
private val data = Data(123)
private val repository = mock<DataRepository>{
on { getCurrentData() } doReturn data
}
@Test
fun `testA`() = runTest {
// given
whenever(time.currentTime).doReturn(flowOf(0))
// when
val tested = CurrentDataProvider(time, repository)
// then
tested.currentData.test {
val item = awaitItem()
item shouldBe data
}
verify(repository, times(1)).getCurrentData()
}
@Test
fun `testB`() = runTest {
// given
whenever(time.currentTime).doReturn(flowOf(0, 1, 2, 3))
// when
val tested = CurrentDataProvider(time, repository)
// then
tested.currentData.test {
val item = awaitItem()
item shouldBe data
}
verify(repository, times(1)).getCurrentData()
}
@Test
fun `testC`() = runTest {
// given
whenever(time.currentTime).doReturn(flowOf(1, 2, 3, 11))
// when
val tested = CurrentDataProvider(time, repository)
// then
tested.currentData.test {
awaitItem()
val item = awaitItem()
item shouldBe data
}
verify(repository, times(2)).getCurrentData()
}
I tried to:
- add
withContext(Dispatchers.Default)
- add
MainDispatcherRule
:
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
@OptIn(ExperimentalCoroutinesApi::class)
class MainDispatcherRule(
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : TestWatcher() {
override fun starting(description: Description) {
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description) {
Dispatchers.resetMain()
}
}
每~5次测试仍不合格。
尝试将协程作用域注入到CurrentDataProvider的构造函数中,而不是将其初始化为字段变量。
测试a可以写成@Test
fun `testA`() = runTest {
val testScope = TestCoroutineScope()
whenever(time.currentTime).doReturn(flowOf(0))
val tested = CurrentDataProvider(time, repository, testScope)
tested.currentData.test {
val item = awaitItem
item shouldBe data
}
verify(repository, times(1)).getCurrentData()
testScope.cleanupTestCoroutines() // Add this line to clean up the test scope
}
还有,检查SonarLint插件,它可以找到像字段初始化的coroutineScopes而不是构造器注入的代码气味