Tengo un ViewModel que habla de un caso de uso y obtiene un flujo, es decir, Flow<MyResult> . Quiero hacer una prueba unitaria de mi ViewModel. Soy nuevo en el uso del flujo. Necesito ayuda por favor. Aquí está el modelo de vista a continuación:
class MyViewModel(private val handle: SavedStateHandle, private val useCase: MyUseCase) : ViewModel() { private val viewState = MyViewState() fun onOptionsSelected() = useCase.getListOfChocolates(MyAction.GetChocolateList).map { when (it) { is MyResult.Loading -> viewState.copy(loading = true) is MyResult.ChocolateList -> viewState.copy(loading = false, data = it.choclateList) is MyResult.Error -> viewState.copy(loading = false, error = "Error") } }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)MyViewState se ve así:
data class MyViewState( val loading: Boolean = false, val data: List<ChocolateModel> = emptyList(), val error: String? = null )La prueba unitaria se ve a continuación. La afirmación falla siempre, no sé qué estoy haciendo mal allí.
class MyViewModelTest { @get:Rule val instantExecutorRule = InstantTaskExecutorRule() private val mainThreadSurrogate = newSingleThreadContext("UI thread") private lateinit var myViewModel: MyViewModel @Mock private lateinit var useCase: MyUseCase @Mock private lateinit var handle: SavedStateHandle @Mock private lateinit var chocolateList: List<ChocolateModel> private lateinit var viewState: MyViewState @Before fun setup() { MockitoAnnotations.initMocks(this) Dispatchers.setMain(mainThreadSurrogate) viewState = MyViewState() myViewModel = MyViewModel(handle, useCase) } @After fun tearDown() { Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher mainThreadSurrogate.close() } @Test fun onOptionsSelected() { runBlocking { val flow = flow { emit(MyResult.Loading) emit(MyResult.ChocolateList(chocolateList)) } Mockito.`when`(useCase.getListOfChocolates(MyAction.GetChocolateList)).thenReturn(flow) myViewModel.onOptionsSelected().observeForever {} viewState.copy(loading = true) assertEquals(viewState.loading, true) viewState.copy(loading = false, data = chocolateList) assertEquals(viewState.data.isEmpty(), false) assertEquals(viewState.loading, true) } } }Creo que he encontrado una mejor manera de probar esto, usando Channel y la función de extensión consumeAsFlow . Al menos en mis pruebas, parece que puedo probar múltiples valores enviados a través del canal (consumidos como flujo).
Entonces... supongamos que tiene algún componente de caso de uso que expone un Flow<String> . En su ViewModelTest , desea verificar que cada vez que se emite un valor, el estado de la interfaz de usuario se actualiza a algún valor. En mi caso, el estado de la interfaz de usuario es un StateFlow , pero esto también debería poder realizarse con LiveData. Además, estoy usando MockK, pero también debería ser fácil con Mockito.
Dado esto, así es como se ve mi prueba:
@Test fun test() = runBlocking(testDispatcher) { val channel = Channel<String>() every { mockedUseCase.someDataFlow } returns channel.consumeAsFlow() channel.send("a") assertThat(viewModelUnderTest.uiState.value, `is`("a")) channel.send("b") assertThat(viewModelUnderTest.uiState.value, `is`("b")) } EDITAR: Supongo que también puede usar cualquier tipo de implementación de flujo caliente en lugar de Channel y consumeAsFlow . Por ejemplo, puede usar un MutableSharedFlow que le permita emit valores cuando lo desee.