androidkotlinandroid-jetpack-composeandroid-jetpack-compose-testing

how to test android compose mutableStateOf like flow Turbine


I following the new google guide and end with this to represent the state in viewmodel like tis

var uiState: AccountSettingUiState by
    mutableStateOf(AccountSettingUiState.Initial)
        private set

then I have this function

 fun resetPasswordUseCase(context: Context) {
         resetPasswordUseCase.execute(context)
            .subscribeOn(rxSchedulers.io)
            .doOnSubscribe {
               uiState  = AccountSettingUiState.Loading
            }
            .observeOn(rxSchedulers.ui)
            .subscribe {
               uiState  = AccountSettingUiState.Result

  }
}

I want to test this function by assert emitting loading then result but how I can capture the values


Solution

  • I can test only final state

    ViewModel

    @HiltViewModel
    class SearchViewModel @Inject constructor(
        private val searchItemUseCase: SearchItemUseCase,
        private val searchUIMapper: SearchUIMapper
    ) : ViewModel() {
    
        var search by mutableStateOf<StateUI<SearchUI>>(StateUI.Init())
    
        fun searchItem(query: String) {
            viewModelScope.launch {
                search = StateUI.Loading()
                searchItemUseCase.execute(query)
                    .catch { error ->
                        search = StateUI.Error(error)
                    }
                    .map {
                        searchUIMapper.map(it)
                    }.collect {
                        search = if (it.results.isEmpty()) {
                            StateUI.Error(EmptySearchException())
                        } else {
                            StateUI.Success(it)
                        }
                    }
            }
        }
    
    }
    

    Test

    @ExperimentalCoroutinesApi
    @ExperimentalTime
    class SearchViewModelTest {
    
        private lateinit var searchViewModel: SearchViewModel
    
        @get:Rule
        var instantTaskExecutorRule = InstantTaskExecutorRule()
    
        @get:Rule
        var coroutineRule = TestCoroutineRule()
    
        @MockK(relaxed = true)
        private lateinit var searchItemUseCase: SearchItemUseCase
    
        private val searchUIMapper by lazy {
            SearchUIMapper(ItemUIMapper())
        }
    
        @Before
        fun setup() {
            MockKAnnotations.init(this)
            searchViewModel = SearchViewModel(
                searchItemUseCase,
                searchUIMapper
            )
        }
    
        @Test
        fun success() = coroutineRule.runBlockingTest {
    
            val search = getSearchEntity(itemEntityList)
            //Given
            coEvery { searchItemUseCase.execute("") } returns flow {
                emit(search)
            }
    
            searchViewModel.searchItem("")
    
            assertEquals(StateUI.Success(searchUIMapper.map(search)), searchViewModel.search)
        }
    
        @Test
        fun emptySearch() = coroutineRule.runBlockingTest {
            val search = getSearchEntity(emptyList())
            //Given
            coEvery { searchItemUseCase.execute("") } returns flow {
                emit(search)
            }
    
            //When
            searchViewModel.searchItem("")
    
            //Verify
            assertTrue((searchViewModel.search as StateUI.Error<SearchUI>).data is EmptySearchException)
        }
    
        @Test
        fun error() = coroutineRule.runBlockingTest {
    
            val error = NoConnectivityException()
            //Given
            coEvery { searchItemUseCase.execute("") } returns flow {
                throw error
            }
    
            //When
            searchViewModel.searchItem("")
    
            assertEquals(StateUI.Error<SearchUI>(error), searchViewModel.search)
        }
    
    }