Android: ViewModel test using kotlin coroutines not working

538 Views Asked by At

I'm using coroutines for the first time and I'm having troubles testing my work on a ViewModel. The test fails with

Wanted but not invoked:
observer.onChanged(<Capturing argument>);

The test is the following:

val androidVersioningMock: Versioning.AndroidVersioning = mock {
    on { required } doAnswer { "3.3.6" }
}
val versioningMock: Versioning = mock {
    on { android } doAnswer { androidVersioningMock }
}
whenever(networkManager.getAppVersionAsync()).thenReturn(GlobalScope.async { versioningMock })
runBlocking {
    updateVersionModel =
        UpdateViewModel(application, coroutineDispatcherProvider).apply {
            updateLiveData.observeForever(stateObserver)
        }
    verify(stateObserver).onChanged(stateCaptor.capture())
    assertTrue(stateCaptor.lastValue is UpdateState.NoUpdate)
    assertEquals(UpdateState.NoUpdate, stateCaptor.lastValue)
}

I have mocked the coroutineDispatcherProvider with

@ExperimentalCoroutinesApi
override val coroutineDispatcherProvider = mock<CoroutineDispatcherProvider> {
    on { main } doAnswer { TestCoroutineContext() }
    on { io } doAnswer { TestCoroutineContext() }
}

And in my ViewModel, the method that fails is

private suspend fun getUpdateVersion(): Versioning =
    withContext(coroutineDispatcherProvider.io) {
        networkManager.getAppVersionAsync().await()
    }

which is executed like :

launch {
    val versioningModel = getUpdateVersion()
    ...
}

Am I not mocking something or not doing something? Thanks in advance!

2

There are 2 best solutions below

0
tynn On BEST ANSWER

The TestCoroutineContext dispatcher is useful to handle timing within tests, but you want to run asynchronous calls synchronously. You should be able to achieve this with the Unconfined dispatcher.

0
Vahab Ghadiri On

I believe better approach is to pass dispatcher to viewModels, so tests are super easy.. see this https://stackoverflow.com/a/62629410/2719243