我有一个视图模型,它接受初始
ViewState
对象,并具有可公开访问的 state
变量,可以收集。
class MyViewModel<ViewState>(initialState: ViewState) : ViewModel() {
val state: StateFlow<ViewState> = MutableStateFlow(initialState)
val errorFlow: SharedFlow<String> = MutableSharedFlow()
init {
performNetworkCall()
}
private fun performNetworkCall() = viewModelScope.launch {
Network.makeCall(
"/someEndpoint",
onSuccess = {
(state as MutableStateFlow).tryEmit(<some new state>)
},
onError = {
(errorFlow as MutableSharedFlow).tryEmit("network failure")
}
)
}
}
从片段观察此状态时,我可以看到初始状态(例如正在加载),并且当网络调用成功完成时我会收集更改(例如,到已加载状态)
但是,我不知道如何从我的
ViewModelUnitTest
观察这种发射。
我使用 kotlin 涡轮机来测试我的状态和共享流的排放,但我只能观察在调用 viewModel.state.test
或 viewModel.errorFlow.test
之后发生的排放。
由于我无法在 ViewModel 初始化之前引用
viewModel.state
或 viewModel.errorFlow
,因此如何编写测试来验证我的初始化逻辑是否正确执行并根据 Network.makeCall
的模拟行为发出预期结果 - 无论是是 state
的新发射还是 errorFlow
发射?
由于您的网络类在视图模型中没有任何引用,因此不可能模拟/伪造
Network
类的行为。因此,为 Network
创建一个参考。要捕获 init
块中发生的情况,您可以在测试类中延迟初始化视图模型。 here中描述了第二种方式。粗略地说,您的课程类似于:
class MyViewModel<ViewState>(
initialState: ViewState,
network: Network
) : ViewModel() {
val state: StateFlow<ViewState> = MutableStateFlow(initialState)
val errorFlow: SharedFlow<String> = MutableSharedFlow()
init {
performNetworkCall()
}
private fun performNetworkCall() = viewModelScope.launch {
network.makeCall(
"/someEndpoint",
onSuccess = {
(state as MutableStateFlow).tryEmit(<some new state>)
},
onError = {
(errorFlow as MutableSharedFlow).tryEmit("network failure")
}
)
}
}
@ExperimentalCoroutinesApi
class MyViewModelTest {
@get:Rule
var coroutinesTestRule = CoroutinesTestRule()
val viewModel by lazy { MyViewModel(/*pass arguments*/) }
}
CoroutineTestRule
可以在这里找到。
在测试中,您应该模拟网络并延迟其响应。还要确保 viewModel 使用 TestDispatcher:
class MyViewModelTest() {
val dispatcher = TestCoroutineDispatcher()
@Before
fun setup() { // ensure that viewModel is using TestDispatcher
Dispatchers.setMain(dispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
internal class WatchedListsViewModelTest {
@Test
fun `should close with error when fetch lists from repository fails`() = runTest {
val initialState = // ...
val network: Network = mockk {
on { makeCall(any(), any(), any()) } doAnswer { delay(1) } // delay to give time for observer to subscribe
}
val viewModel = MyViewModel(initalState, network)
val result = viewModel.state.take(1).toList()
advanceUntilIdle()
assertEquals("expected value", result.first())
}
}
}