So, I am using coroutines 1.6 and in my data sourc...
# coroutines
j
So, I am using coroutines 1.6 and in my data source I have this method
Copy code
override fun sumSelectedFees(): Single<Float> = rxSingle(workDispatcher) {
    dao.findDeliveryModeShippingOptions(true)
        .fold(0f) { total, shippingOption -> total + shippingOption.feeValue }
}
and I trying to call this in my test
Copy code
@Test
fun `should sum all inserted fees for delivery`() = runTest {
    val dataSource = dataSource = DeliveryModesDataSourceImpl(dao, UnconfinedTestDispatcher())
    dataSource.sumSelectedFees()
        .test()
        .assertValue(0f)
}
but my test is failing since the test is not waiting the rx call
.test()
to complete.. I Used
UnconfinedTestDispatcher
here, but I am not sure what I dispatcher I should be using in this case.
s
I'm guessing the problem is the
workDispatcher
you're passing to
rxSingle
. The test doesn't know anything about that dispatcher and so can't wait for it to finish.
You'll need to find a way to pass the dispatcher from the test into the
sumSelectedFees
method.
One option would be:
Copy code
override fun sumSelectedFees(context: CoroutineContext = workDispatcher): Single<Float> = rxSingle(context) {
    ...
j
I do pass the Dispatcher in the constructor
Copy code
val dataSource = DeliveryModesDataSourceImpl(dao, UnconfinedTestDispatcher())
s
Yeah, I just realised that 🤦 sorry
👍 1
In that case, I think the problem is just that the
UnconfinedTestDispatcher
you use isn't the same one being used by
runTest
Try to get the existing dispatcher from the
TestScope
and pass it to the
DeliveryModesDataSourceImpl
instead of creating a new
UnconfinedTestDispatcher
Or create your dispatcher outside the test and pass it into both
runTest
and the
DeliveryModesDataSourceImpl
j
I changed my code to use
Copy code
private val dispatcher = UnconfinedTestDispatcher()
Copy code
dataSource = DeliveryModesDataSourceImpl(dao, dispatcher)
Copy code
runTest(dispatcher) {...
Still not working
s
Maybe after the
.test()
and before the
.assertValue()
you need to call
runCurrent()
Even the
UnconfinedTestDispatcher
won't always run all of its coroutines without a call to
runCurrent
, if they're nested/launched in more complicated ways
j
I tried it too, didn't help.
s
I'm out of ideas then 🤷 sorry! Although I've tested a lot of coroutines, I haven't worked with
rxSingle
. There must be a trick we're missing.
j
Me too, this is so frustrating
d
Try
UnconfinedTestDispatcher(testScheduler)
instead of just
UnconfinedTestDispatcher
. Otherwise, there's no link between
runTest
and the dispatcher, unless there's
Dispatchers.setMain
somewhere.
j
I am not calling
Dispatchers.setMain
, but I am using
runTest(dispatcher)
, where
val dispatcher = UnconfinedTestDispatcher()
d
Then
UnconfinedTestDispatcher()
just creates a new dispatcher, not related to
runTest
.
j
Sorry, I think I didn't understand what you mean. I am just trying to make the test work by connecting the dots... I need the
rxSingle
to publish in a sync way so
.test()
will be sync and so my test. I passed around the Dispatcher to every place I could find, but my tests are still failing.
d
Copy code
@Test
fun `should sum all inserted fees for delivery`() = runTest {
    val dataSource = dataSource = DeliveryModesDataSourceImpl(dao, UnconfinedTestDispatcher(testScheduler)) // <--- modified here
    dataSource.sumSelectedFees()
        .test()
        .assertValue(0f)
}
j
My results are the same.
Copy code
expected: 0.0 (class: Float) but was: [] (latch = 1, values = 0, errors = 0, completions = 0)
java.lang.AssertionError: expected: 0.0 (class: Float) but was: [] (latch = 1, values = 0, errors = 0, completions = 0)
I think I was able to reproduce the problem with a simpler/more generic version of the code
Copy code
private val dispatcher = UnconfinedTestDispatcher()

suspend fun bar() = withContext(dispatcher) {
    withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
        1
    }

}

@Test
fun foo() = runTest(dispatcher) {
    rxSingle(dispatcher) { bar() }
        .test()
        .assertValue(1)
        .assertComplete()
}
d
This isn't really related to coroutines, this is due to
test()
in RxJava not awaiting the completion. For example, here is the same issue, without any coroutines:
Copy code
@Test
    fun foo2() {
        val single = Single.create<Int> { emitter ->
            thread {
                Thread.sleep(10)
                emitter.onSuccess(1)
            }
        }
        single.test().assertValue(1).assertComplete()
    }
It may work if there's only one thread, but
<http://Dispatchers.IO|Dispatchers.IO>
uses other threads. This can be fixed with
Copy code
@Test
fun foo() = runTest(dispatcher) {
    rxSingle(dispatcher) { bar() }
        .test()
        .await() // <-------- waits for completion before checking the results
        .assertValue(1)
        .assertComplete()
}
j
Ohh, thanks! I didn't know that!