0

In my ViewModel there's a StateFlow which seems to prevent my unit test from ever completing, i.e. the test "hangs". I'm fairly new to the Flow lib and not sure how to cancel/disable said StateFlow so I can run my test as normal.

I created a simplified version of the code to highlight my problem. Here's the ViewModel in question:

@ExperimentalCoroutinesApi
class MyViewModel(
    private val someApiClient: SomeApiClient,
    private val dispatchers: CoroutineContexts = DefaultCoroutineContexts,
    private val someLogger: SomeLogger
) : ViewModel() {

    private val queries = MutableSharedFlow<String>()

    @FlowPreview
    val suggestions: StateFlow<Result<Throwable, String>> =
        queries
            .sample(THROTTLE_TIME)
            .distinctUntilChanged()
            .mapLatest {
                try {
                    val downloadedSuggestions = someApiClient.getSuggestions(it)
                    Result.Success(downloadedSuggestions)
                } catch (exception: Throwable) {
                    Result.Error(exception)
                }
            }
            .flowOn(dispatchers.io)
            .stateIn(viewModelScope, SharingStarted.Eagerly, Result.Success(""))

    fun dispatchEvent(event: MyEvent) {
        when (event) {
            is SearchEvent -> someLogger.logStuff()
        }
    }
}

And the test looks like this:

@ExperimentalCoroutinesApi
class MyViewModelTest {

    @get:Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
    @get:Rule val coroutinesTestRule = MainDispatcherRule()

    @Mock lateinit var someApiClient: SomeApiClient
    @Mock lateinit var someLogger: SomeLogger

    lateinit var myViewModel: MyViewModel

    @Before
    fun setUp() {
        myViewModel = MyViewModel(
            someApiClient,
            coroutinesTestRule.testDispatcher.createCoroutineContexts(),
            someLogger
        )
    }

    @Test
    fun `my test`() = runTest {
        // When
        myViewModel.dispatchEvent(SearchEvent)

        // Then
        verify(someLogger).logStuff()
    }
}

I tried a suggestion from the Android Developer documentation page in the test:

@Test
fun `my test`() = runTest {
    // Given
    val dispatcher = UnconfinedTestDispatcher(testScheduler)
    val job = launch(dispatcher) { myViewModel.suggestions.collect() }

    // When
    myViewModel.dispatchEvent(SearchEvent)

    // Then
    verify(someLogger).logStuff()
    job.cancel()
}

But it didn't help. I feel like I'm missing something fairly obvious but can't quite put my finger on it. Any suggestions are welcome.

JakeP
  • 1,736
  • 4
  • 23
  • 31

0 Answers0