e

    emmano

    2 years ago
    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.
    c

    curioustechizen

    2 years ago
    Is
    runBlockingTest
    still JVM only? This is not obvious from the docs. There's
    kotlinx.coroutines.test
    module which is JVM only but it is obsolete and deprecated: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.test/index.html Then, there's
    kotlinx-coroutines-test
    module which is the replacement. And it doesn't mention any platform requirements. I did see something about
    ServiceLoader
    in its docs so I'm not really sure. https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/
    e

    emmano

    2 years ago
    I believe
    runBlockingTest()
    is still JVM only
    e

    emmano

    2 years ago
    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.
    Kris Wong

    Kris Wong

    2 years ago
    true. you just need a common
    expect runBlocking
    e

    emmano

    2 years ago
    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
    Kris Wong

    Kris Wong

    2 years ago
    you have the same implementation on both platforms? what is the implementation?
    e

    emmano

    2 years ago
    actual fun <T> test(block: suspend (CoroutineScope) -> T) {
        runBlocking(SynchronousDispatcher()) {
            block(this)
        }
    }
    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
    Kris Wong

    Kris Wong

    2 years ago
    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

    2 years ago
    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.
    Kris Wong

    Kris Wong

    2 years ago
    why are you using your own dispatcher instead of the default?
    use the defaults
    e

    emmano

    2 years ago
    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?
    Kris Wong

    Kris Wong

    2 years ago
    you do not need to specify the context parameter
    e

    emmano

    2 years ago
    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)
    Kris Wong

    Kris Wong

    2 years ago
    internal expect fun <T> runBlocking(
        context: CoroutineContext = EmptyCoroutineContext,
        block: suspend CoroutineScope.() -> T
    )T
    e

    emmano

    2 years ago
    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.
    Kris Wong

    Kris Wong

    2 years ago
    that's exactly what runBlocking does. i've never seen it not wait for all coroutines to complete
    e

    emmano

    2 years ago
    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.