https://kotlinlang.org logo
#coroutines
Title
# coroutines
z

zak.taccardi

12/19/2018, 9:56 PM
thread here
@Sam your problem is
uiDispatcher : CoroutineDispatcher = Main
change it to
uiDispatcher : CoroutineDispatcher
then you cannot inject the wrong dispatcher
or better:
s

Sam

12/19/2018, 9:57 PM
Main was a default for application run
z

zak.taccardi

12/19/2018, 9:57 PM
dispatcher : CoroutineContext
s

Sam

12/19/2018, 9:57 PM
in tests, i do override it
s

streetsofboston

12/19/2018, 9:57 PM
Did you try using the
TestCoroutineContext
, which has a Dispatcher that you can control by moving virtual time?
s

Sam

12/19/2018, 9:57 PM
Copy code
private val viewModel = MainFragmentViewModel( appsRepository, Dispatchers.Unconfined )
z

zak.taccardi

12/19/2018, 9:57 PM
I use
Dispatchers.Default
for my view models
s

Sam

12/19/2018, 9:58 PM
No, i haven't used TestCoroutineContext
z

zak.taccardi

12/19/2018, 9:58 PM
but you can use
Dispatchers.Main
if you prefer
s

Sam

12/19/2018, 9:58 PM
But again, i don't think the problem is with the dispatcher
the code is like this
unit test
Copy code
runBlocking {}
calling view model function (regular function)
z

zak.taccardi

12/19/2018, 9:59 PM
mockito will give weird failrues
if something isn’t mocked properly
it’s often non-obvious
s

Sam

12/19/2018, 9:59 PM
which is launching another coroutine using the view model scope
this coroutine invokes the actual suspend function
s

streetsofboston

12/19/2018, 10:00 PM
Which suspend function?
s

Sam

12/19/2018, 10:00 PM
in unit test, i'm trying to verify that the suspend function is actually invoked
Copy code
suspend fun updateCurrentInstalledApps( installedPackages : List<String> ) = withContext( IO ) {
       appsDao.updateCurrentInstalledApps( installedPackages )
    }
s

streetsofboston

12/19/2018, 10:00 PM
Your unit test code is an empty `runBlocking {}`…
s

Sam

12/19/2018, 10:01 PM
here is the actual code for test
Copy code
@Test
    fun `onDisableAllAppsClicked is relayed to repository`() {

        runBlocking {
            viewModel.onDisableAllAppsClicked()
            verify( appsRepository ).updateAllAppsExcludedStatus( true )
        }
    }
Copy code
private val appsRepository = mock<AppsRepository>()
    private val viewModel = MainFragmentViewModel( appsRepository, Dispatchers.Unconfined )
s

streetsofboston

12/19/2018, 10:02 PM
This test just launches the coroutine in your ViewModel and then immediately return. There is no blocking code in your test-code (not in your test, not in your Viewmodel)
s

Sam

12/19/2018, 10:03 PM
yes, thats another problem
how do i wait for the launched coroutine to finish first?
s

streetsofboston

12/19/2018, 10:04 PM
You use the
<http://Dispatchers.IO|Dispatchers.IO>
, which will run on a background thread. This one doesn’t do really anything, since your test-function returns almost immediately.
Inject your Dispatchers, both for
Main
and
IO
. Either use, for your tests, the Unconfined one or use the TestCoroutineContext for more granular control.
s

Sam

12/19/2018, 10:09 PM
In my case, IO is not directly used in viewmodel, instead is used in repository. Anyways, keeping that aside, wouldn't the continuation still be different?
and by continuation, i'm talking about the hidden parameter passed to the suspend function hosted in my repository
s

streetsofboston

12/19/2018, 10:11 PM
That’s right… you only check if the
updateAllAppsExcludedStatus
is called or not… I wonder if the
suspend
is not handled well by Mockito, since the actual JVM signature is quire different from when it would have been non-suspend.
s

Sam

12/19/2018, 10:13 PM
yeah, mockito just uses it as yet another parameter and fails as the one used in verify will be different from the one that was actually invoked
runBlocking{...} vs viewModelScope.launch{ ... }
s

streetsofboston

12/19/2018, 10:13 PM
Do you use Mockito or Mockito-Kotlin?
s

Sam

12/19/2018, 10:13 PM
tried both
Copy code
testImplementation 'org.mockito:mockito-core:2.22.0'
    testImplementation 'com.nhaarman:mockito-kotlin:1.5.0'
s

streetsofboston

12/19/2018, 10:14 PM
s

Sam

12/19/2018, 10:18 PM
Thanks, version 2 did it
with this, my test works as expected
Copy code
testCompile 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0'
though i still have to work on making sure launched coroutines finish first
Thanks a ton @streetsofboston
s

streetsofboston

12/19/2018, 10:22 PM
Glad that it worked out for you! 🙂
d

dave08

12/20/2018, 3:47 AM
Even though it might be fixed, you can always switch to mockk.io, it's much better for Kotlin and coroutines... @Sam
s

Sam

12/20/2018, 4:06 AM
Thx, i had initially looked at it just to open classes but found a kotlin compiler plugin to workaround. I will try this one out.
2 Views