Search code examples
androidandroid-viewmodelkotlin-flowturbineandroid-unit-testing

Kotlin flow test not emitting all value


I am very new in Unit testing , i am trying to test my flow using Turbine library, it is not emitting all value, here is my test

fun `send function should emit Loading and Content states`() = runTest {
    // Arrange
    val userProfile = UserProfile(login = "test_login")
    val contentState = UiState.Content(userProfile)


    coEvery {
        fakeRepository.getDetail(any())
    } returns userProfile


    // Act
    viewModel.send(userProfile.login!!)


    // Assert
    testScheduler.advanceUntilIdle()
    viewModel.uiState.test {
        assertEquals(UiState.Loading, awaitItem())
        assertEquals(contentState, awaitItem())
        cancelAndIgnoreRemainingEvents()
    }
}

and ViewModel is here, keep failing with reason

Expected UiState$Loading Actual :Content

    viewModelScope.launch(dispatchers.main) {
        flow {
            emit(UiState.Loading)
            val mResponse = userRepository.getDetail(login = login)
            emit(UiState.Content(mResponse))
        }.catch {
            UiState.Error(it.message.toString())
            it.printStackTrace()
        }.flowOn(dispatchers.main).collect {
            _uiState.value = it
        }
    }

Solution

  • Finally I got solution of my problem, I changed my dispatcher from UnConfinedTestDispatcher to StandardTestDispatcher, it starts working

    standardtestdispatcher and unconfinedtestdispatcher

    Here is my test code:

    @OptIn(ExperimentalCoroutinesApi::class)
    class TestDispatchers : DispatcherProvider {
    private val testDispatcher = StandardTestDispatcher()
    override val main: CoroutineDispatcher
        get() = testDispatcher
    override val io: CoroutineDispatcher
        get() = testDispatcher
    override val default: CoroutineDispatcher
        get() = testDispatcher
    }
    
    @OptIn(ExperimentalCoroutinesApi::class)
    @RunWith(MockitoJUnitRunner::class)
    class UserProfileViewModelTest {
    
    // private var fakeRepository = FakeRepository()
    
    private val testDispatchers = TestDispatchers()
    private val fakeRepository = mockk<UserRepository>()
    private val fakeDB = mockk<IRoomdatabase>()
    private val viewModel =
        UserProfileViewModel(
            dispatchers = testDispatchers,
            userRepository = fakeRepository,
            userDatabase = fakeDB
        )
    //private val coroutineScope = TestScope(testDispatchers.main)
    
    @Before
    fun setUP() {
          Dispatchers.setMain(testDispatchers.main)
    }
    
    @Test
    fun `send function should emit Loading and Content states`() = runTest {
        // Arrange
        val login = "test_login"
        val userProfile = UserProfile(login = login)
        val contentState = UiState.Content(userProfile)
    
    
        coEvery {
            fakeRepository.getDetail(login)
        } returns userProfile
    
    
        viewModel.send(userProfile.login!!)
        // Assert
    
        assertTrue(viewModel.uiState.value == UiState.Empty)
        viewModel.uiState.test {
            //  coroutineScope.testScheduler.advanceUntilIdle()
            assertEquals(UiState.Empty, awaitItem())
            assertEquals(UiState.Loading, awaitItem())
            assertEquals(contentState, awaitItem())
            //cancelAndIgnoreRemainingEvents()
        }
    
        coVerify { fakeRepository.getDetail(login) }
    }