https://kotlinlang.org logo
e

emmano

04/19/2020, 9:05 PM
Hello all. I have ported a Kotlin JVM library to be a multiplatform library and now I am also trying to migrate its tests. The JVM tests pass as expected, but the KN tests do not. Originally the JVM tests used
TestCoroutineScope
and
TestCoroutineDispatcher
to control delays, how can the same kind of test be written for KN? Namely, it seems that
runBlockingTest{}
is a JVM only construct. Probably because it uses
DelayController
which is not available in KN (or so it seems). Here is the test class. The first test passes in both platforms, The second one only passes in JVM land. Any help will be appreciated.
The failure in KN seems to be related to my
Dispatcher
not having an
EventLoop
since at soon as the code reaches a
delay()
I get the "There is no event loop. Use runBlocking { ... } to start one" message. That makes sense because it is a custom dispatcher that executes things as they come. Using this custom dispatcher allowed me to not use
runBlockingTest
in JVM side of things.
I believe
runBlockingTest()
is still JVM only
e

emmano

04/22/2020, 1:30 PM
Thanks for the link @Kris Wong, but it I am no sure how his article helps with testing coroutines? The issue is that there does not seem to be a
runBlockingTest()
equivalent for Kotlin Native. I am new to KMP, I am sorry if I am missing something.
k

Kris Wong

04/22/2020, 1:31 PM
true. you just need a common
expect runBlocking
e

emmano

04/22/2020, 1:32 PM
and that is what I am doing, but it only seems to work on the JVM…
in fact I even have the same
actual
implementation for both platforms, meaning I am not using
rubBlockingTest{}
on the JVM
k

Kris Wong

04/22/2020, 1:53 PM
you have the same implementation on both platforms? what is the implementation?
e

emmano

04/22/2020, 2:33 PM
Copy code
actual fun <T> test(block: suspend (CoroutineScope) -> T) {
    runBlocking(SynchronousDispatcher()) {
        block(this)
    }
}
Copy code
class SynchronousDispatcher : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        block.run()
    }
}
this allows for my JVM test to pass as expected without using
runBlockingTest()
on the JVM side
the goal was to have both platforms use the same
actual
I am trying to tests classes that use
Flows
it all seems to work fine until you try to test something that calls
delay()
on the KN side
All this code is in the repo I linked if you want to take a look
k

Kris Wong

04/22/2020, 2:38 PM
Copy code
internal actual fun <T> runBlocking(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
)T = kotlinx.coroutines.runBlocking(context, block)
slack is swallowing a colon but you get the point
e

emmano

04/22/2020, 3:44 PM
What would the
CoroutineContext
be for the Kotlin Native side?
Meaning what
Dispatcher
should I use?
The snippet you shared above seems to be the same as the one I am using with the difference being that I set a Dispatcher instead of having the caller of
runBlocking()
do so.
k

Kris Wong

04/22/2020, 3:50 PM
why are you using your own dispatcher instead of the default?
use the defaults
e

emmano

04/22/2020, 3:53 PM
by default
Dispatcher
you mean
Dispatchers.Main
? What I mean is, on the actual test I need to specify a
CoroutineContext
with your version of
runBlocking()
, what should that be?
k

Kris Wong

04/22/2020, 3:55 PM
you do not need to specify the context parameter
e

emmano

04/22/2020, 4:02 PM
so, what this means is that
context
will be provided by the framework? Meaning by declaring the
expect
runBlocking()
with
context
as a parameter,
context
will be somehow be passed in to each individual platform?
(sorry for all the questions, I am trying to understand)
k

Kris Wong

04/22/2020, 4:20 PM
Copy code
internal expect fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> T
)T
e

emmano

04/22/2020, 4:33 PM
That makes all of my tests fail since the
Dispatcher
does not wait for all coroutines to complete. Thanks for the help though, I appreciate it.
k

Kris Wong

04/22/2020, 5:01 PM
that's exactly what runBlocking does. i've never seen it not wait for all coroutines to complete
e

emmano

04/24/2020, 1:55 PM
yeah, my issue is in TestFlow. I am trying to create a class that would allow me to record emissions of a
Flow
so I can later assert them. Similar to how
.test()
works in Rx. The implementation I have works in the JVM, but not in KN.