I have a class like this
class SomeClass { fun someFun() { // ... Some synchronous code async { suspendfun() } } private suspend fun suspendFun() { dependency.otherFun().await() // ... other code } }
I want to unit test someFun()
so I wrote a unit test that looks like this:
@Test fun testSomeFun() { runBlocking { someClass.someFun() } // ... verifies & asserts }
But this doesn't seem to work because runBlocking doesn't actually block execution until everything inside runBlocking is done. If I test suspendFun()
directly inside runBlocking
it works as expected but I want to be able to test someFun()
all together.
Any clue how to test a function with both sync and async code?
We just have to use the suspend keyword. Note: Suspend functions are only allowed to be called from a coroutine or another suspend function. You can see that the async function which includes the keyword suspend. So, in order to use that, we need to make our function suspend too.
Suspending functions are at the center of everything coroutines. A suspending function is simply a function that can be paused and resumed at a later time. They can execute a long running operation and wait for it to complete without blocking.
Note that these xxxAsync functions are not suspending functions. They can be used from anywhere. However, their use always implies asynchronous (here meaning concurrent) execution of their action with the invoking code. // but waiting for a result must involve either suspending or blocking.
As implemented, your someFun()
will just “fire and forget” the async
result. As a result, runBlocking
does not make a difference in that test.
If possible, make someFun()
return async
's Deferred
and then, in runBlocking
, call await
on it.
fun someFun(): Deferred<Unit> { // ... Some synchronous code return async { suspendFun() } }
Then the test:
runBlocking { SomeClass().someFun().await() }
This question/answer is a good resource for further information.
It's also possible to avoid async
in favor of using suspend
functions and a launch
-created coroutine:
suspend fun someFun() { // ... Some synchronous code suspendFun() } private suspend fun suspendFun() { delay(1000) println("executed") // ... other code }
The test uses launch
and the outer runBlocking
implicitly waits for its completion:
val myScope = GlobalScope runBlocking { myScope.launch { SomeClass().someFun() } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With