Kotlin 1.4.21我有一个非常简单的ViewModel,它使用coroutine和stateFlow。但是,单元测试将失败,因为stateFlow似乎没有得到更新。
我认为这是因为测试将在stateFlow更新之前完成。
expected not to be empty
这是我的ViewModel正在测试中
class TrendingSearchViewModel @Inject constructor(
private val loadTrendingSearchUseCase: LoadTrendingSearchUseCase,
private val coroutineDispatcher: CoroutineDispatcherProvider
) : ViewModel() {
private val trendingSearchMutableStateFlow = MutableStateFlow<List<String>>(emptyList())
val trendingSearchStateFlow = trendingSearchMutableStateFlow.asStateFlow()
fun getTrendingSearch() {
viewModelScope.launch(coroutineDispatcher.io()) {
try {
trendingSearchMutableStateFlow.value = loadTrendingSearchUseCase.execute()
} catch (exception: Exception) {
Timber.e(exception, "trending ${exception.localizedMessage}")
}
}
}
}这是我的真正的考试课,我试过不同的方法让它开始工作。
class TrendingSearchViewModelTest {
private val loadTrendingSearchUseCase: LoadTrendingSearchUseCase = mock()
private val coroutineDispatcherProvider = CoroutineDispatcherProviderImp()
private lateinit var trendingSearchViewModel: TrendingSearchViewModel
@Before
fun setUp() {
trendingSearchViewModel = TrendingSearchViewModel(
loadTrendingSearchUseCase,
coroutineDispatcherProvider
)
}
@Test
fun `should get trending search suggestions`() {
runBlocking {
// Arrange
val trending1 = UUID.randomUUID().toString()
val trending2 = UUID.randomUUID().toString()
val trending3 = UUID.randomUUID().toString()
whenever(loadTrendingSearchUseCase.execute()).thenReturn(listOf(trending1, trending2, trending3))
val job = launch {
trendingSearchViewModel.trendingSearchStateFlow.value
}
// Act
trendingSearchViewModel.getTrendingSearch()
// Assert
val result = trendingSearchViewModel.trendingSearchStateFlow.value
assertThat(result).isNotEmpty()
job.cancel()
}
}
}这是我在测试中嘲笑的用法:
class LoadTrendingSearchUseCaseImp @Inject constructor(
private val searchCriteriaProvider: SearchCriteriaProvider,
private val coroutineDispatcherProvider: CoroutineDispatcherProvider
) : LoadTrendingSearchUseCase {
override suspend fun execute(): List<String> {
return withContext(coroutineDispatcherProvider.io()) {
searchCriteriaProvider.provideTrendingSearch().trendingSearches
}
}
}万一需要的话,这就是我的界面:
interface CoroutineDispatcherProvider {
fun io(): CoroutineDispatcher = Dispatchers.IO
fun default(): CoroutineDispatcher = Dispatchers.Default
fun main(): CoroutineDispatcher = Dispatchers.Main
fun immediate(): CoroutineDispatcher = Dispatchers.Main.immediate
fun unconfined(): CoroutineDispatcher = Dispatchers.Unconfined
}
class CoroutineDispatcherProviderImp @Inject constructor() : CoroutineDispatcherProvider发布于 2022-03-09 15:54:10
这就是对我有用的东西:
@Test
fun `should get trending search suggestions`() {
runBlockingTest {
// Arrange
val trending1 = UUID.randomUUID().toString()
val trending2 = UUID.randomUUID().toString()
val trending3 = UUID.randomUUID().toString()
val listOfTrending = listOf(trending1, trending2, trending3)
whenever(loadTrendingSearchUseCase.execute()).thenReturn(listOfTrending)
/* List to collect the results */
val listOfEmittedResult = mutableListOf<List<String>>()
val job = launch {
trendingSearchViewModel.trendingSearchStateFlow.toList(listOfEmittedResult)
}
// Act
trendingSearchViewModel.getTrendingSearch()
// Assert
assertThat(listOfEmittedResult).isNotEmpty()
verify(loadTrendingSearchUseCase).execute()
job.cancel()
}
}发布于 2022-03-09 08:42:44
我认为,当您需要更复杂的场景时,这个由杰克·沃顿( Jack )编写的库https://github.com/cashapp/turbine将会有很大的帮助。
我认为正在发生的是,在片段中调用.collect { },这确保了流的启动。检查终端操作符定义:流上的终端操作符是挂起启动流的集合的函数。https://kotlinlang.org/docs/flow.html#terminal-flow-operators
对于sharedFlow来说则不是这样,它可能被配置为急切地启动。
所以为了解决你的问题,你可以打电话给
val job = launch {
trendingSearchViewModel.trendingSearchStateFlow.collect()
}https://stackoverflow.com/questions/71406113
复制相似问题