suspendCoroutine is not testtable
See original GitHub issueConsider following scenario
Function to test:
fun execute(appConfig: AppConfig): Deferred<String> = async {
registerDevice(appConfig.serviceId)
}
private suspend fun registerDevice(serviceId: String): String = suspendCoroutine {
//some nasty 3rd party library delivering data in a callback
lib.registerDeviceForService(object: Callback {
override fun onSuccess(response) {
it.resume(response.data)
}
}
)
}
Here is the test:
private val callbackCaptor = argumentCaptor<Callback<Response>>()
@Test
fun `ok result of registration results in success`() = blocking {
val deferred = classToTest.execute(appConfig)
mockRegisterResponseSuccess()
val result = deferred.await()
result.should.be.equal(trackingId)
}
private fun mockRegisterResponseSuccess(result: ApiResponseStatus) {
then(lib).should().registerDeviceForService(callbackCaptor.capture())
callbackCaptor.firstValue.onSuccess(Response(data = "testdata"))
}
suspendCoroutine
blocks the call and code never gets to the mockRegisterReponseSuccess
call to mock and capture the callback
Issue Analytics
- State:
- Created 5 years ago
- Comments:11 (3 by maintainers)
Top Results From Across the Web
How to wait for suspendCoroutine in unit test? - Stack Overflow
When using suspendCoroutine, the Dispatchers.Unconfined will not be suspended and later resumed, but instead will simply return.
Read more >Best practices for coroutines in Android
This page presents several best practices that have a positive impact by making your app more scalable and testable when using coroutines.
Read more >Playing with Kotlin in Android: coroutines and how to get rid of ...
In this post I will talk about how Kotlin coroutines allows you to get rid of the callback hell in your Android code...
Read more >Unit Testing Coroutine Suspend Functions using ... - craigrussell
Specifically, the test will fail with the following reason: java.lang.IllegalStateException: This job has not completed yet at kotlinx.
Read more >[Solved]-Callback and Co-routine on AWS SDK-kotlin
SignInResult = suspendCoroutine { continuation -> signIn(userName, password, someParam, ... I'm not sure if ResultResponse is your own class. In case it is, ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
for posterity: using MockK you can write something like this val lib: Lib = mockk() val slot = slot<LibCallback>() every { lib.doStuff(capture(slot)) } answers { slot.captured.onResultReady(“result ready”) }
Why are you expecting this test to work? You call
await
(suspending the whole coroutine) and then whendeferred
is complete, you are mocking the service which is supposed to complete deferred. That’s exactly the case I’ve described above withCompletableFuture
. You should first mock the service and only then callawait
on it.About mocking Kotlin code – it’s not a
kotlinx.coroutines
issue, just use a Kotlin-mockito.Something like this modulo proper mocking