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

Marco Righini

01/20/2021, 10:53 PM
Hello, I had to use coroutines reactive but I ran in a strange behavior while testing. This is a minimal not working example
Copy code
class CoroutinesReactiveTest {

    private val dep = mock<Dep>()
    private val underTest = UnderTest(dep)

    @Test
    fun `Test someOtherMethod called`() = runBlocking {
        underTest.methodToTest().test()

        verify(dep).someMethod() // Commenting this line test passes
        verify(dep).suspendingFun()
        verify(dep).someOtherMethod()
    }
}

class Dep {

    fun someMethod() {
        println("someMethod called")
    }

    suspend fun suspendingFun() {
        delay(100)
        println("suspendingFun called")
    }

    fun someOtherMethod() {
        println("someOtherMethod called")
    }
}

class UnderTest(private val dep: Dep) {

    fun methodToTest(): Completable {
        return Completable.fromCallable { dep.someMethod() }
            .andThen(rxCompletable { dep.suspendingFun() })
            .andThen(Completable.fromCallable { dep.someOtherMethod() })
    }
}
commenting the first verify test passes
m

Marcelo Hernandez

01/22/2021, 9:06 PM
One thing to note is that Rx’s
test()
is not a blocking function as far as I’m aware, so it will return immediately. However, both
rxCompletable
and
delay()
operate on
Dispatchers.Default
by default. So I’m wondering if somehow
test()
is returning immediately but your
delay()
is operating asynchronously by the time you run your
verify()
calls.
Yes the
runBlocking
will wait for all
suspend
code to return but I don’t think it guarantees that these suspend functions return before you make the
verify
calls.
I would also suggest you swap out
runBlocking
for
runBlockingTest
so that your tests do not actually
delay
.
m

Marco Righini

01/24/2021, 7:51 AM
Dependency is mocked so the test doesn't depend on the implementation of the suspending function.
m

Marcelo Hernandez

01/26/2021, 12:10 AM
Ok, but the test still ends up calling
rxCompletable
right?
If
rxCompletable { … }
is used with all the default parameters, it will use
Dispatchers.Default
. Which means in your test environment, there will be thread hopping. I have had to work around this by explicitly passing in a
Dispatcher
which I have set up as part of my Dependency Injection graph.
Copy code
// Where dispatchers is my own wrapper around static Dispatchers
rxCompletable(dispatchers.default) { … }
3 Views