'viewModel unit test with flow

I'm trying to write a unit test for my viewModel and I haven't figure out how to do it. I am using kotlin flow and jetpack compose. In the viewModel, the function changes de value of a mutableState list but when I call it on the test I don't know how to retrieve the value.

below is my view model, repository, fake repository and the test I have so far. How can I do it?

@HiltViewModel
class MarvelViewModel @Inject constructor(
    private val repository: MarvelRepository
) : ViewModel() {

    val result: MutableState<List<Result>> = mutableStateOf(listOf())

 fun searchCharacter(
        name: String,
        context: Context
    ) {
        viewModelScope.launch {

            val ts = System.currentTimeMillis().toString()
            val md = MessageDigest.getInstance("MD5")
            val input = ts + PRIVATE_KEY + PUBLIC_KEY
            val hash = BigInteger(1, md.digest(input.toByteArray())).toString(16)
            val offset = if (page.value == 1) 0 else PAGE_SIZE * page.value

            repository.searchCharacter(
                name = name,
                limit = PAGE_SIZE,
                offset = offset,
                ts = ts,
                apikey = PUBLIC_KEY,
                hash = hash
            ).onEach { dataState ->
                loading.value = dataState.loading

                dataState.data?.let {
                    Log.d(DEBUG_TAG, it.toString())
                    if (it.data.total > PAGE_SIZE) {
                        val rest = it.data.total % PAGE_SIZE
                        numPages.value = (it.data.total + rest) / PAGE_SIZE
                    }
                    nameSearch.value = textSearch.value
                    result.value = it.data.results

                    pageNumberList.value = (1..numPages.value).toList()

                }

                dataState.error?.let {
                    Toast.makeText(context, "Falha ao carregar", Toast.LENGTH_SHORT).show()

                }
            }.launchIn(viewModelScope)
        }
    }
class MarvelRepositoryImpl @Inject constructor(
    private val retrofitService: RetrofitService
) : MarvelRepository {

    override suspend fun searchCharacter(
        name: String,
        limit: Int,
        offset: Int,
        ts: String,
        apikey: String,
        hash: String
    ): Flow<DataState<Response>> = flow {
        try {
            emit(DataState.loading())

            val response = retrofitService.search(name, limit, offset, ts, apikey, hash)
            emit(DataState.success(response))
        } catch (e: Exception) {
            emit(DataState.error<Response>(e.message ?: "Unknown Error"))
        }

    }
}
class FakeRepositoryImpl : MarvelRepository {

    override suspend fun searchCharacter(
        name: String,
        limit: Int,
        offset: Int,
        ts: String,
        apikey: String,
        hash: String
    ): Flow<DataState<Response>> = flow {
        emit(DataState.loading())
        emit(DataState.success(response))
    }
}

val response = Response(
    data = Data(
        count = 4, limit = 4, offset = 0, results = listOf(
            Result(
                description = "Wounded", id = 1009368, name = "Iron Man",
                thumbnail = Thumbnail(
                    extension = "jpg",
                    path = "http://i.annihil.us/u/prod/marvel/i/mg/9/c0/527bb7b37ff55"
                )
            ),
            Result(
                description = "", id = 1017320, name = "Iron Man (Iron Man 3 - The Official Game)",
                thumbnail = Thumbnail(
                    extension = "jpg",
                    path = "http://i.annihil.us/u/prod/marvel/i/mg/9/03/5239c1408c936"
                )
            ),
            Result(
                description = "", id = 1017294, name = "Iron Man (LEGO Marvel Super Heroes)",
                thumbnail = Thumbnail(
                    extension = "jpg",
                    path = "http://i.annihil.us/u/prod/marvel/i/mg/6/90/5239c3cc8a259"
                )
            ),
            Result(
                description = "", id = 1017310, name = "Iron Man (Marvel Heroes)",
                thumbnail = Thumbnail(
                    extension = "jpg",
                    path = "http://i.annihil.us/u/prod/marvel/i/mg/9/40/5239be60a67da"
                )
            )
        ), total = 7
    ), status = "Ok"
)
@ExperimentalCoroutinesApi
class MarvelViewModelTest {

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    var mainCoroutineRule = MainCoroutineRule()

    val context = mock(Context::class.java)

    private lateinit var viewModel: MarvelViewModel

    private lateinit var repository: FakeRepositoryImpl

    @Before
    fun setup(){
        repository = FakeRepositoryImpl()
        viewModel = MarvelViewModel(repository)
    }

    @Test
    fun `should return a flow of response`() = runBlockingTest {

        viewModel.searchCharacter("iron man", context)
        
    }
}


Solution 1:[1]

coroutineRule.runBlockingTest block flow until finish FakeRepositoryImpl

you could use

@Test
    fun success() = coroutineRule.runBlockingTest {
        //when
        viewModel.searchCharacter("iron man", context)
        //verify
        assertEquals(response.data.results, searchViewModel.result)
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Emmanuel Montt